30
28
from provisioningserver.import_images.download_descriptions import (
31
29
download_all_image_descriptions,
33
from provisioningserver.import_images.helpers import (
31
from provisioningserver.import_images.download_resources import (
32
download_all_boot_resources,
37
from provisioningserver.import_images.product_mapping import ProductMapping
34
from provisioningserver.import_images.helpers import logger
35
from provisioningserver.import_images.product_mapping import map_products
38
36
from provisioningserver.utils import (
44
from simplestreams.contentsource import FdContentSource
45
from simplestreams.mirrors import (
49
from simplestreams.objectstores import FileStore
50
from simplestreams.util import (
57
44
class NoConfigFile(Exception):
58
45
"""Raised when the config file for the script doesn't exist."""
61
def boot_reverse(boot_images_dict):
62
"""Determine the subarches supported by each boot resource.
64
Many subarches may be deployed by a single boot resource. We note only
65
subarchitectures here and ignore architectures because the metadata format
66
tightly couples a boot resource to its architecture.
68
We can figure out for which architecture we need to use a specific boot
69
resource by looking at its description in the metadata. We can't do the
70
same with subarch, because we may want to use a boot resource only for a
71
specific subset of subarches.
73
This function returns the relationship between boot resources and
74
subarchitectures as a `ProductMapping`.
76
:param boot: A `BootImageMapping` containing the images' metadata.
77
:return: A `ProductMapping` mapping products to subarchitectures.
79
reverse = ProductMapping()
80
for image, boot_resource in boot_images_dict.items():
81
reverse.add(boot_resource, image.subarch)
85
48
def tgt_entry(arch, subarch, release, label, image):
86
49
"""Generate tgt target used to commission arch/subarch with release
119
class RepoWriter(BasicMirrorWriter):
120
"""Download boot resources from an upstream Simplestreams repo.
124
:ivar product_dict: A `ProductMapping` describing the desired boot
128
def __init__(self, root_path, cache_path, info):
129
self._root_path = os.path.abspath(root_path)
130
self.product_dict = info
131
# XXX jtv 2014-04-11: FileStore now also takes an argument called
132
# complete_callback, which can be used for progress reporting.
133
self._cache = FileStore(os.path.abspath(cache_path))
134
super(RepoWriter, self).__init__()
136
def write(self, path, keyring=None):
137
(mirror, rpath) = path_from_mirror_url(path, None)
138
policy = get_signing_policy(rpath, keyring)
139
reader = UrlMirrorReader(mirror, policy=policy)
140
super(RepoWriter, self).sync(reader, rpath)
142
def load_products(self, path=None, content_id=None):
145
def filter_version(self, data, src, target, pedigree):
146
return self.product_dict.contains(products_exdata(src, pedigree))
148
def insert_file(self, name, tag, checksums, size, contentsource):
149
logger.info("Inserting file %s (tag=%s, size=%s).", name, tag, size)
151
tag, contentsource, checksums, mutable=False, size=size)
152
return [(self._cache._fullpath(tag), name)]
154
def insert_root_image(self, tag, checksums, size, contentsource):
155
root_image_tag = 'root-image-%s' % tag
156
root_image_path = self._cache._fullpath(root_image_tag)
157
root_tgz_tag = 'root-tgz-%s' % tag
158
root_tgz_path = self._cache._fullpath(root_tgz_tag)
159
if not os.path.isfile(root_image_path):
160
logger.info("New root image: %s.", root_image_path)
162
tag, contentsource, checksums, mutable=False, size=size)
163
uncompressed = FdContentSource(
164
GzipFile(self._cache._fullpath(tag)))
165
self._cache.insert(root_image_tag, uncompressed, mutable=False)
166
self._cache.remove(tag)
167
if not os.path.isfile(root_tgz_path):
168
logger.info("Converting root tarball: %s.", root_tgz_path)
169
call_uec2roottar(root_image_path, root_tgz_path)
170
return [(root_image_path, 'root-image'), (root_tgz_path, 'root-tgz')]
172
def insert_item(self, data, src, target, pedigree, contentsource):
173
item = products_exdata(src, pedigree)
174
checksums = item_checksums(data)
175
tag = checksums['sha256']
177
ftype = item['ftype']
178
if ftype == 'root-image.gz':
179
links = self.insert_root_image(tag, checksums, size, contentsource)
181
links = self.insert_file(
182
ftype, tag, checksums, size, contentsource)
183
for subarch in self.product_dict.get(item):
184
dst_folder = os.path.join(
185
self._root_path, item['arch'], subarch, item['release'],
187
if not os.path.exists(dst_folder):
188
os.makedirs(dst_folder)
189
for src, link_name in links:
190
link_path = os.path.join(dst_folder, link_name)
191
if os.path.isfile(link_path):
193
os.link(src, link_path)
196
82
def install_boot_loaders(destination):
197
83
"""Install the all the required file from each bootloader method.
198
84
:param destination: Directory where the loaders should be stored.
327
188
logger.warn("Can't import: no Simplestreams sources configured.")
330
boot = download_all_image_descriptions(config)
191
image_descriptions = download_all_image_descriptions(config)
192
if image_descriptions.is_empty():
333
194
"No boot resources found. Check configuration and connectivity.")
336
197
storage = config['boot']['storage']
337
meta_file_content = boot.dump_json()
198
meta_file_content = image_descriptions.dump_json()
338
199
if meta_contains(storage, meta_file_content):
339
200
# The current maas.meta already contains the new config. No need to
340
201
# rewrite anything.
343
reverse_boot = boot_reverse(boot)
344
snapshot_path = compose_snapshot_path(storage)
345
cache_path = os.path.join(storage, 'cache')
204
product_mapping = map_products(image_descriptions)
206
snapshot_path = download_all_boot_resources(
207
sources, storage, product_mapping)
346
208
targets_conf = os.path.join(snapshot_path, 'maas.tgt')
347
writer = RepoWriter(snapshot_path, cache_path, reverse_boot)
349
for source in sources:
350
writer.write(source['path'], source['keyring'])
352
210
targets_conf_content = compose_targets_conf(snapshot_path)