Merge lp:~ltrager/maas-images/bootloaders_part2 into lp:maas-images

Proposed by Lee Trager
Status: Merged
Merged at revision: 325
Proposed branch: lp:~ltrager/maas-images/bootloaders_part2
Merge into: lp:maas-images
Diff against target: 485 lines (+231/-104)
3 files modified
conf/bootloaders.yaml (+21/-13)
meph2/commands/dpkg.py (+133/-8)
meph2/commands/meph2_util.py (+77/-83)
To merge this branch: bzr merge lp:~ltrager/maas-images/bootloaders_part2
Reviewer Review Type Date Requested Status
Scott Moser (community) Approve
Review via email: mp+303241@code.launchpad.net

Description of the change

To post a comment you must log in.
324. By Lee Trager

Put os in bootloader product name

325. By Lee Trager

Use bootloaders from Xenial

Revision history for this message
Lee Trager (ltrager) wrote :

As discussed on IRC pull the bootloaders from Xenial. The sample output has been updated.

Revision history for this message
Scott Moser (smoser) wrote :

the data looks good.

i have one nit pick in the conf/bootloaders.yaml
I think better to have:

   files:
    - usr/lib/grub/arm64-efi

than

   files:
    - /usr/lib/grub/arm64-efi

just to make it clear that the files are coming from the package itself and not the isntalled system, and also to avoid the pitfall of:
  os.path.join("/home/smoser", "/etc/passwd") == "/etc/passwd"

review: Approve
326. By Lee Trager

Remove leading / from bootloader files

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'conf/bootloaders.yaml'
--- conf/bootloaders.yaml 2016-08-23 15:17:16 +0000
+++ conf/bootloaders.yaml 2016-08-24 20:40:59 +0000
@@ -1,35 +1,41 @@
1product_id: "com.ubuntu.maas.daily:1:bootloaders-bases:{bootloader}"1product_id: "com.ubuntu.maas.daily:1:{os}:{firmware_platform}:{arch}"
2content_id: "com.ubuntu.maas:daily:1:bootloaders-bases-download"2content_id: "com.ubuntu.maas:daily:1:bootloader-download"
33
4bootloaders:4bootloaders:
5 - bootloader: pxe5 - firmware-platform: pxe
6 packages:6 packages:
7 - pxelinux7 - pxelinux
8 - syslinux-common8 - syslinux-common
9 arch: amd649 arch: i386
10 arches: i386,amd64
10 archive: http://archive.ubuntu.com/ubuntu11 archive: http://archive.ubuntu.com/ubuntu
11 release: xenial12 release: xenial
13 os: pxelinux
12 files:14 files:
13 - /usr/lib/PXELINUX/pxelinux.015 - usr/lib/PXELINUX/pxelinux.0
14 - /usr/lib/syslinux/modules/bios/*.c3216 - usr/lib/syslinux/modules/bios/*.c32
15 - bootloader: uefi17 - firmware-platform: uefi
16 packages:18 packages:
17 - shim-signed19 - shim-signed
18 - grub-efi-amd64-signed20 - grub-efi-amd64-signed
19 arch: amd6421 arch: amd64
22 arches: amd64
20 archive: http://archive.ubuntu.com/ubuntu23 archive: http://archive.ubuntu.com/ubuntu
21 release: xenial24 release: xenial
25 os: grub-efi-signed
22 files:26 files:
23 - /usr/lib/shim/shim.efi.signed, bootx64.efi27 - usr/lib/shim/shim.efi.signed, bootx64.efi
24 - /usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed, grubx64.efi28 - usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed, grubx64.efi
25 - bootloader: uefi-arm6429 - firmware-platform: uefi
26 packages:30 packages:
27 - grub-efi-arm64-bin31 - grub-efi-arm64-bin
28 arch: arm6432 arch: arm64
33 arches: arm64
29 archive: http://ports.ubuntu.com34 archive: http://ports.ubuntu.com
30 release: xenial35 release: xenial
36 os: grub-efi
31 files:37 files:
32 - /usr/lib/grub/arm64-efi/38 - usr/lib/grub/arm64-efi/
33 grub_format: arm64-efi39 grub_format: arm64-efi
34 grub_output: grubaa64.efi40 grub_output: grubaa64.efi
35 grub_config: |41 grub_config: |
@@ -41,14 +47,16 @@
41 # Failed to load based on MAC address.47 # Failed to load based on MAC address.
42 # Load arm64 by default, UEFI only supported by 64-bit48 # Load arm64 by default, UEFI only supported by 64-bit
43 configfile (pxe)/grub/grub.cfg-default-arm6449 configfile (pxe)/grub/grub.cfg-default-arm64
44 - bootloader: powerkvm50 - firmware-platform: open-firmware
45 packages:51 packages:
46 - grub-ieee1275-bin52 - grub-ieee1275-bin
47 arch: ppc64el53 arch: ppc64el
54 arches: ppc64el,ppc64
48 archive: http://ports.ubuntu.com55 archive: http://ports.ubuntu.com
49 release: xenial56 release: xenial
57 os: grub-ieee1275
50 files:58 files:
51 - /usr/lib/grub/powerpc-ieee1275/59 - usr/lib/grub/powerpc-ieee1275/
52 grub_format: powerpc-ieee127560 grub_format: powerpc-ieee1275
53 grub_output: bootppc64.bin61 grub_output: bootppc64.bin
54 grub_config: |62 grub_config: |
5563
=== modified file 'meph2/commands/dpkg.py'
--- meph2/commands/dpkg.py 2016-08-03 13:00:03 +0000
+++ meph2/commands/dpkg.py 2016-08-24 20:40:59 +0000
@@ -6,6 +6,7 @@
6import os6import os
7import re7import re
8import sys8import sys
9import tarfile
9import tempfile10import tempfile
10import glob11import glob
1112
@@ -128,13 +129,98 @@
128 pkg_path = os.path.join(dest, os.path.basename(package['Filename']))129 pkg_path = os.path.join(dest, os.path.basename(package['Filename']))
129 with open(pkg_path, 'wb') as stream:130 with open(pkg_path, 'wb') as stream:
130 stream.write(pkg_data)131 stream.write(pkg_data)
132 package['files'] = []
133 output = subprocess.check_output(['dpkg', '-c', pkg_path])
134 for line in output.decode('utf-8').split('\n'):
135 # The file is the last column in the list.
136 file_info = line.split()
137 # Last line is just a newline
138 if len(file_info) == 0:
139 continue
140 if file_info[-1].startswith('./'):
141 # Remove leading './' if it exists
142 f = file_info[-1][2:]
143 elif file_info[-1].startswith('/'):
144 # Removing leading '/' if it exists
145 f = file_info[-1][1:]
146 else:
147 f = file_info[-1]
148 if f != '':
149 package['files'].append(f)
131 return package150 return package
132151
133152
153def get_file_info(f):
154 size = 0
155 sha256 = hashlib.sha256()
156 with open(f, 'rb') as f:
157 for chunk in iter(lambda: f.read(2**15), b''):
158 sha256.update(chunk)
159 size += len(chunk)
160 return sha256.hexdigest(), size
161
162
163def make_item(ftype, src_file, dest_file, stream_path, src_packages):
164 sha256, size = get_file_info(dest_file)
165 for src_package in src_packages:
166 if src_file in src_package['files']:
167 return {
168 'ftype': ftype,
169 'sha256': sha256,
170 'path': stream_path,
171 'size': size,
172 'src_package': src_package['src_package'],
173 'src_version': src_package['src_version'],
174 'src_release': src_package['src_release'],
175 }
176 raise ValueError("%s not found in src_packages" % src_file)
177
178
179def archive_files(items, target):
180 """Archive multiple files from a src_package into archive.tar.xz."""
181 archive_items = {}
182 new_items = {}
183 # Create a mapping of source packages and the files that came from them.
184 for item in items.values():
185 key = "%(src_package)s-%(src_release)-%(src_version)" % item
186 if archive_items.get(key) is None:
187 archive_items[key] = {
188 'src_package': item['src_package'],
189 'src_release': item['src_release'],
190 'src_version': item['src_version'],
191 'files': [item['path']],
192 }
193 else:
194 archive_items[key]['files'].append(item['path'])
195 for item in archive_items.values():
196 stream_path = os.path.join(
197 os.path.dirname(item['files'][0]),
198 '%s.tar.xz' % item['src_package'])
199 full_path = os.path.join(target, stream_path)
200 tar = tarfile.open(full_path, 'w:xz')
201 for f in item['files']:
202 item_full_path = os.path.join(target, f)
203 tar.add(item_full_path, os.path.basename(item_full_path))
204 os.remove(item_full_path)
205 tar.close()
206 sha256, size = get_file_info(full_path)
207 new_items[item['src_package']] = {
208 'ftype': 'archive.tar.xz',
209 'sha256': sha256,
210 'path': stream_path,
211 'size': size,
212 'src_package': item['src_package'],
213 'src_release': item['src_release'],
214 'src_version': item['src_version'],
215 }
216 return new_items
217
218
134def extract_files_from_packages(219def extract_files_from_packages(
135 archive, packages, architecture, files, release, dest,220 archive, packages, architecture, files, release, target, path,
136 grub_format=None, grub_config=None):221 grub_format=None, grub_config=None, grub_output=None):
137 tmp = tempfile.mkdtemp(prefix='maas-images-')222 tmp = tempfile.mkdtemp(prefix='maas-images-')
223 src_packages = []
138 for package in packages:224 for package in packages:
139 package = get_package(archive, package, architecture, release, tmp)225 package = get_package(archive, package, architecture, release, tmp)
140 pkg_path = os.path.join(tmp, os.path.basename(package['Filename']))226 pkg_path = os.path.join(tmp, os.path.basename(package['Filename']))
@@ -142,7 +228,23 @@
142 sys.stderr.write('%s not found in archives!' % package)228 sys.stderr.write('%s not found in archives!' % package)
143 sys.exit(1)229 sys.exit(1)
144 subprocess.check_output(['dpkg', '-x', pkg_path, tmp])230 subprocess.check_output(['dpkg', '-x', pkg_path, tmp])
145231 new_source_package = True
232 for src_package in src_packages:
233 if src_package['src_package'] == package['Source']:
234 new_source_package = False
235 src_package['files'] += package['files']
236 if new_source_package:
237 # Some source packages include the package version in the source
238 # name. Only take the name, not the version.
239 src_package = package['Source'].split(' ')[0]
240 src_packages.append({
241 'src_package': src_package,
242 'src_version': package['Version'],
243 'src_release': release,
244 'files': package['files'],
245 })
246 dest = os.path.join(target, path)
247 items = {}
146 if grub_format is None:248 if grub_format is None:
147 for i in files:249 for i in files:
148 if '*' in i or '?' in i:250 if '*' in i or '?' in i:
@@ -150,20 +252,38 @@
150 src = "%s/%s" % (tmp, i)252 src = "%s/%s" % (tmp, i)
151 unglobbed_files = glob.glob(src)253 unglobbed_files = glob.glob(src)
152 for f in unglobbed_files:254 for f in unglobbed_files:
153 dest_file = "%s/%s" % (dest, os.path.basename(f))255 basename = os.path.basename(f)
256 dest_file = "%s/%s" % (dest, basename)
257 stream_path = "%s/%s" % (path, basename)
154 shutil.copyfile(f, dest_file)258 shutil.copyfile(f, dest_file)
259 pkg_file = f[len(tmp):]
260 while pkg_file.startswith('/'):
261 pkg_file = pkg_file[1:]
262 items[basename] = make_item(
263 'bootloader', pkg_file, dest_file, stream_path,
264 src_packages)
155 elif ',' in i:265 elif ',' in i:
156 # Copy the a file from the package using a new name266 # Copy the a file from the package using a new name
157 src_file, dest_file = i.split(',')267 src_file, dest_file = i.split(',')
158 src_file = "%s/%s" % (tmp, src_file.strip())268 dest_file = dest_file.strip()
159 dest_file = "%s/%s" % (dest, dest_file.strip())269 full_src_file_path = "%s/%s" % (tmp, src_file.strip())
160 shutil.copyfile(src_file, dest_file)270 stream_path = "%s/%s" % (path, dest_file)
271 full_dest_file_path = "%s/%s" % (dest, dest_file)
272 shutil.copyfile(full_src_file_path, full_dest_file_path)
273 items[dest_file] = make_item(
274 'bootloader', src_file, full_dest_file_path, stream_path,
275 src_packages)
161 else:276 else:
162 # Straight copy277 # Straight copy
278 basename = os.path.basename(i)
163 src_file = "%s/%s" % (tmp, i)279 src_file = "%s/%s" % (tmp, i)
164 dest_file = "%s/%s" % (dest, os.path.basename(src_file))280 dest_file = "%s/%s" % (dest, basename)
281 stream_path = "%s/%s" % (path, basename)
165 shutil.copyfile(src_file, dest_file)282 shutil.copyfile(src_file, dest_file)
283 items[basename] = make_item(
284 'bootloader', i, dest_file, stream_path, src_packages)
166 else:285 else:
286 dest = os.path.join(dest, grub_output)
167 # You can only tell grub to use modules from one directory287 # You can only tell grub to use modules from one directory
168 modules_path = "%s/%s" % (tmp, files[0])288 modules_path = "%s/%s" % (tmp, files[0])
169 modules = []289 modules = []
@@ -189,4 +309,9 @@
189 '-O', grub_format,309 '-O', grub_format,
190 '-d', modules_path,310 '-d', modules_path,
191 ] + modules)311 ] + modules)
312 basename = os.path.basename(dest)
313 stream_path = "%s/%s" % (path, basename)
314 items[basename] = make_item(
315 'bootloader', files[0], dest, stream_path, src_packages)
192 shutil.rmtree(tmp)316 shutil.rmtree(tmp)
317 return archive_files(items, target)
193318
=== modified file 'meph2/commands/meph2_util.py'
--- meph2/commands/meph2_util.py 2016-08-01 19:15:59 +0000
+++ meph2/commands/meph2_util.py 2016-08-24 20:40:59 +0000
@@ -1,7 +1,7 @@
1#!/usr/bin/python31#!/usr/bin/python3
22
3import argparse3import argparse
4import glob4from datetime import datetime
5import copy5import copy
6import os6import os
7from functools import partial7from functools import partial
@@ -176,9 +176,13 @@
176 (pedigree, sutil.products_exdata(176 (pedigree, sutil.products_exdata(
177 src, pedigree, include_top=False,177 src, pedigree, include_top=False,
178 insert_fieldnames=False)),)178 insert_fieldnames=False)),)
179
179 return super(BareMirrorWriter, self).insert_item(180 return super(BareMirrorWriter, self).insert_item(
180 data, src, target, pedigree, contentsource)181 data, src, target, pedigree, contentsource)
181182
183 def remove_item(self, data, src, target, pedigree):
184 return
185
182 def remove_version(self, data, src, target, pedigree):186 def remove_version(self, data, src, target, pedigree):
183 # sync doesnt filter on things to be removed, so187 # sync doesnt filter on things to be removed, so
184 # we have to do that here.188 # we have to do that here.
@@ -187,9 +191,6 @@
187191
188 self.removed_versions.append(pedigree)192 self.removed_versions.append(pedigree)
189193
190 def remove_item(self, data, src, target, pedigree):
191 return
192
193 def insert_products(self, path, target, content):194 def insert_products(self, path, target, content):
194 # insert_item and insert_products would not be strictly necessary195 # insert_item and insert_products would not be strictly necessary
195 # they're here, though, to keep a list of those things appended.196 # they're here, though, to keep a list of those things appended.
@@ -231,7 +232,9 @@
231232
232 sutil.products_condense(233 sutil.products_condense(
233 self.tproducts,234 self.tproducts,
234 sticky=['di_version', 'kpackage', 'sha256', 'md5', 'path'])235 sticky=[
236 'di_version', 'kpackage', 'sha256', 'md5', 'path', 'ftype',
237 'src_package', 'src_version', 'src_release'])
235238
236 self.tproducts['updated'] = sutil.timestamp()239 self.tproducts['updated'] = sutil.timestamp()
237240
@@ -525,100 +528,91 @@
525 }528 }
526529
527530
528def get_file_info(f):
529 size = 0
530 sha256 = hashlib.sha256()
531 with open(f, 'rb') as f:
532 for chunk in iter(lambda: f.read(2**15), b''):
533 sha256.update(chunk)
534 size += len(chunk)
535 return sha256.hexdigest(), size
536
537
538def import_bootloaders(args, product_tree, cfgdata):531def import_bootloaders(args, product_tree, cfgdata):
539 for bootloader in cfgdata['bootloaders']:532 for firmware_platform in cfgdata['bootloaders']:
540 product_id = cfgdata['product_id'].format(533 product_id = cfgdata['product_id'].format(
541 bootloader=bootloader['bootloader'])534 os=firmware_platform['os'],
542 package = get_package(535 firmware_platform=firmware_platform['firmware-platform'],
543 bootloader['archive'], bootloader['packages'][0],536 arch=firmware_platform['arch'])
544 bootloader['arch'], bootloader['release'])537 # Compile a list of the latest packages in the archive this bootloader
545538 # pulls files from
546 if (539 src_packages = {}
547 product_id in product_tree['products'] and540 for package in firmware_platform['packages']:
548 package['Version'] in product_tree['products'][product_id][541 package_info = get_package(
549 'versions']):542 firmware_platform['archive'], package,
543 firmware_platform['arch'], firmware_platform['release'])
544 # Some source packages include the package version in the source
545 # name. Only take the name, not the version.
546 src_package_name = package_info['Source'].split(' ')[0]
547 src_packages[src_package_name] = {
548 'src_version': package_info['Version'],
549 'src_release': firmware_platform['release'],
550 'found': False,
551 }
552 # Check if the bootloader has been built from the latest version of
553 # the packages in the archive
554 if product_id in product_tree['products']:
555 versions = product_tree['products'][product_id]['versions']
556 for data in versions.values():
557 for item in data['items'].values():
558 src_package = src_packages.get(item['src_package'])
559 if (
560 src_package is not None and
561 src_package['src_version'] == item['src_version']
562 and
563 src_package['src_release'] == item['src_release']):
564 src_packages[item['src_package']]['found'] = True
565 bootloader_uptodate = True
566 for src_package in src_packages.values():
567 if not src_package['found']:
568 bootloader_uptodate = False
569 # Bootloader built from the latest packages already in stream
570 if bootloader_uptodate:
550 print(571 print(
551 "Product %s at version %s exists, skipping" % (572 "Product %s built from the latest package set, skipping"
552 product_id, package['Version']))573 % product_id)
553 continue574 continue
575 # Find an unused version
576 today = datetime.utcnow().strftime('%Y%m%d')
577 point = 0
578 while True:
579 version = "%s.%d" % (today, point)
580 products = product_tree['products']
581 if (
582 product_id not in products or
583 version not in products[product_id]['versions'].keys()):
584 break
585 point += 1
554 if product_tree['products'].get(product_id) is None:586 if product_tree['products'].get(product_id) is None:
555 print("Creating new product %s" % product_id)587 print("Creating new product %s" % product_id)
556 product_tree['products'][product_id] = {588 product_tree['products'][product_id] = {
557 'label': 'daily',589 'label': 'daily',
558 'arch': bootloader['arch'],590 'arch': firmware_platform['arch'],
559 'subarch': 'generic',591 'arches': firmware_platform['arches'],
560 'subarches': 'generic',592 'os': firmware_platform['os'],
561 'os': 'bootloader',593 'bootloader-type': firmware_platform['firmware-platform'],
562 'release': bootloader['bootloader'],
563 'versions': {},594 'versions': {},
564 }595 }
565 path = os.path.join(596 path = os.path.join(
566 'bootloaders', bootloader['bootloader'], bootloader['arch'],597 'bootloaders', firmware_platform['firmware-platform'],
567 package['Version'])598 firmware_platform['arch'], version)
568 dest = os.path.join(args.target, path)599 dest = os.path.join(args.target, path)
569 os.makedirs(dest)600 os.makedirs(dest)
570 grub_format = bootloader.get('grub_format')601 grub_format = firmware_platform.get('grub_format')
571 if grub_format is not None:602 if grub_format is not None:
572 dest = os.path.join(dest, bootloader['grub_output'])603 dest = os.path.join(dest, firmware_platform['grub_output'])
573 print(604 print(
574 "Downloading and creating %s version %s" % (605 "Downloading and creating %s version %s" % (
575 product_id, package['Version']))606 product_id, version))
576 extract_files_from_packages(607 items = extract_files_from_packages(
577 bootloader['archive'], bootloader['packages'],608 firmware_platform['archive'], firmware_platform['packages'],
578 bootloader['arch'], bootloader['files'], bootloader['release'],609 firmware_platform['arch'], firmware_platform['files'],
579 dest, grub_format, bootloader.get('grub_config'))610 firmware_platform['release'], args.target, path, grub_format,
580 if grub_format is not None:611 firmware_platform.get('grub_config'),
581 sha256, size = get_file_info(dest)612 firmware_platform.get('grub_output'))
582 product_tree['products'][product_id]['versions'][613 product_tree['products'][product_id]['versions'][version] = {
583 package['Version']] = {614 'items': items
584 'items': {615 }
585 bootloader['grub_output']: {
586 'ftype': 'bootloader',
587 'sha256': sha256,
588 'path': os.path.join(
589 path, bootloader['grub_output']),
590 'size': size,
591 }
592 }
593 }
594 else:
595 items = {}
596 for i in bootloader['files']:
597 basename = os.path.basename(i)
598 dest_file = os.path.join(dest, basename)
599 if '*' in dest_file or '?' in dest_file:
600 # Process multiple files copied with a wildcard
601 unglobbed_files = glob.glob(dest_file)
602 elif ',' in dest_file:
603 # If we're renaming the file from the package use the new
604 # name.
605 _, basename = i.split(',')
606 basename = basename.strip()
607 dest_file = os.path.join(dest, basename)
608 unglobbed_files = [dest_file]
609 else:
610 unglobbed_files = [dest_file]
611 for f in unglobbed_files:
612 basename = os.path.basename(f)
613 sha256, size = get_file_info(f)
614 items[basename] = {
615 'ftype': 'bootloader',
616 'sha256': sha256,
617 'path': os.path.join(path, basename),
618 'size': size,
619 }
620 product_tree['products'][product_id]['versions'][
621 package['Version']] = {'items': items}
622616
623617
624def main_import(args):618def main_import(args):

Subscribers

People subscribed via source and target branches