Merge lp:~ltrager/maas-images/squashfs into lp:maas-images
- squashfs
- Merge into maas-ephemerals
Status: | Merged |
---|---|
Merged at revision: | 316 |
Proposed branch: | lp:~ltrager/maas-images/squashfs |
Merge into: | lp:maas-images |
Diff against target: |
204 lines (+89/-8) 4 files modified
bin/maas-cloudimg2eph2 (+9/-0) meph2/commands/cloudimg_sync.py (+71/-6) meph2/commands/meph2_util.py (+1/-1) meph2/stream.py (+8/-1) |
To merge this branch: | bzr merge lp:~ltrager/maas-images/squashfs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andres Rodriguez (community) | Approve | ||
Review via email:
|
Commit message
If available build root-image.gz based off the upstream SquashFS image and republish the SquashFS image.
Description of the change
When generating Ubuntu images check if the upstream SimpleStream has published a SquashFS image. If so build root-image.gz based off the SquashFS image, instead of the tar.gz. The downloaded SquashFS image has its SHA256 verified and republished, with the same name as upstream, in the new stream alongside root-image.gz.
This won't be pushed into the image creation process for http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Scott Moser (smoser) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Lee Trager (ltrager) wrote : | # |
> Why do we need to create a root-image.gz anymore ? I don't think we want to
> create any images anymore. If there's a squashfs image, then we should use
> that and not have to create more and more and more stuff.
>
> We are trying to reduce the amount of images and what we have available and
> what we use, creating a root-image.gz adds more stuff that we dont really
> need?
root-image.gz is still needed for older releases until the SquashFS work is done. By having both we can update the stream while still developing the feature. Once the SquashFS work is done and has been pushed out we can remove the root-image.gz.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Lee Trager (ltrager) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Lee Trager (ltrager) wrote : | # |
Last night I created an MP which adds support for SquashFS images to MAAS.
https:/
- 305. By Scott Moser
-
Disable metadata_csum when creating ext4 filesystems
In yakkety, we have sync'd an e2fsprogs version that supports metadata
checksumming in ext4 and enables the feature by default. Unfortunately,
some Launchpad buildds (specifically those for armhf and one for powerpc)
are running on a kernel that is too old to support this, which means an
unusable ext4 partition is created. To work around this issue, we disable
the metadata_csum option when we create an ext4 filesystem.The Launchpad team are working to upgrade the offending build hosts, so we
should be able to back this change out before yakkety releases.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Lee Trager (ltrager) wrote : | # |
I've added a flag(--squashfs) to meph2-cloudimg-
I've also removed the code which downloads the squashfs.manifest file as we don't use it in MAAS.
- 306. By Lee Trager
-
Import and publish bootloaders from a yaml file.
This allows the import subcommand to accept a yaml file containing a list of bootloaders. The latest version of the bootloader is downloaded from the Ubuntu archive. Files can be copied out of the bootloader or they can be processed with grub-mkimage.
This also fixes a bug with the import command where if any version of a product existed no newer version would be imported. This will allow more up-to-date CentOS images to be published.
- 307. By Lee Trager
-
Remove duplicated geturl function and add version to bootloader product listing
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andres Rodriguez (andreserl) wrote : | # |
Questions inline.
- 308. By Lee Trager
-
Allow a grub.cfg to be embedded into generated grub image
- 309. By Lee Trager
-
Allow bootloader files to be renamed when put into the stream
- 310. By Lee Trager
-
Strip renamed filenames
- 311. By Scott Moser
-
tox.ini: tweaks
* recreate: do not recreate tox environments on every 'tox' run.
* create separate tox environments rather than re-using via
declaring the 'envdir'
* flake8: list the directories to run flake8 on so it does
not attempt to run on .tox/ - 312. By Scott Moser
-
dpkg.py: fix flake8 complaints
Simply fix flake8 complaints from dpkg.py.
- 313. By Scott Moser
-
netinst.py: handle identical data published at different paths
the process that builds the d-i netboot data began publishing a
kernel and initramfs at different paths in 20101020ubuntu318.39.
For example, under
/ubuntu-ports/dists/ trusty- updates/ main/installer- arm64/\
20101020ubuntu318. 40/images/ xenial- generic/ netboot/ There are now the same kernel and initrd and kernel at
xenial-generic/ netboot/ initrd. gz
xenial-generic/ netboot/ ubuntu- installer/ arm64/initrd. gz
xenial-generic/ netboot/ ubuntu- installer/ arm64/linux
xenial-generic/ netboot/ vmlinuz That would cause a Name Collision in mine_md error on these files.
The change to address this is to raise exception only if
a name collision has different data than the existing. Also
we specifically handle 'dtb' as currently the basename (derived
from the url) is used there.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Lee Trager (ltrager) wrote : | # |
Responded inline
- 314. By Lee Trager
-
Use /usr/bin/dpkg directly instead of apt_pkg to avoid tox build issues
- 315. By Lee Trager
-
Remove unneeded comparison
- 316. By Lee Trager
-
Add --squashfs flag to meph2-cloudimg-sync
meph2-cloudimg-sync now downloads SquashFS images if available from the upstream source. If the --squashfs flag is used meph2-cloudimg-sync will publish the unmodified SquashFS images where available. The --squashfs flag also disables generating the di-kernel and di-initrd.
Preview Diff
1 | === modified file 'bin/maas-cloudimg2eph2' | |||
2 | --- bin/maas-cloudimg2eph2 2016-07-11 15:18:00 +0000 | |||
3 | +++ bin/maas-cloudimg2eph2 2016-08-01 19:18:07 +0000 | |||
4 | @@ -646,6 +646,15 @@ | |||
5 | 646 | mv --force "$timg" "$output" || | 646 | mv --force "$timg" "$output" || |
6 | 647 | { error "failed move to $output"; return 1; } | 647 | { error "failed move to $output"; return 1; } |
7 | 648 | fi | 648 | fi |
8 | 649 | |||
9 | 650 | if [[ "$src" == *squashfs ]]; then | ||
10 | 651 | output_dir=$(dirname $output) | ||
11 | 652 | cp "$src" "$output_dir" || { | ||
12 | 653 | error "copy SquashFS image from $src -> $output_dir failed" | ||
13 | 654 | return 1; | ||
14 | 655 | } | ||
15 | 656 | fi | ||
16 | 657 | |||
17 | 649 | ddebug 1 "finished" | 658 | ddebug 1 "finished" |
18 | 650 | return 0 | 659 | return 0 |
19 | 651 | } | 660 | } |
20 | 652 | 661 | ||
21 | === modified file 'meph2/commands/cloudimg_sync.py' | |||
22 | --- meph2/commands/cloudimg_sync.py 2016-03-01 16:01:41 +0000 | |||
23 | +++ meph2/commands/cloudimg_sync.py 2016-08-01 19:18:07 +0000 | |||
24 | @@ -11,7 +11,9 @@ | |||
25 | 11 | from meph2.stream import CONTENT_ID, create_version | 11 | from meph2.stream import CONTENT_ID, create_version |
26 | 12 | 12 | ||
27 | 13 | import argparse | 13 | import argparse |
28 | 14 | import glob | ||
29 | 14 | import copy | 15 | import copy |
30 | 16 | import hashlib | ||
31 | 15 | import os | 17 | import os |
32 | 16 | import sys | 18 | import sys |
33 | 17 | import yaml | 19 | import yaml |
34 | @@ -65,7 +67,7 @@ | |||
35 | 65 | 67 | ||
36 | 66 | class CloudImg2Meph2Sync(mirrors.BasicMirrorWriter): | 68 | class CloudImg2Meph2Sync(mirrors.BasicMirrorWriter): |
37 | 67 | def __init__(self, config, out_d, target, v2config, rebuilds=None, | 69 | def __init__(self, config, out_d, target, v2config, rebuilds=None, |
39 | 68 | verbosity=0): | 70 | verbosity=0, squashfs=False): |
40 | 69 | super(CloudImg2Meph2Sync, self).__init__(config=config) | 71 | super(CloudImg2Meph2Sync, self).__init__(config=config) |
41 | 70 | if rebuilds is None: | 72 | if rebuilds is None: |
42 | 71 | rebuilds = {} | 73 | rebuilds = {} |
43 | @@ -74,7 +76,13 @@ | |||
44 | 74 | self.target = target | 76 | self.target = target |
45 | 75 | self.v2config = v2config | 77 | self.v2config = v2config |
46 | 76 | self.filters = self.config.get('filters', []) | 78 | self.filters = self.config.get('filters', []) |
48 | 77 | self.enable_di = self.config.get('enable_di', True) | 79 | self.squashfs = squashfs |
49 | 80 | if self.squashfs: | ||
50 | 81 | # As of MAAS 2.0 DI is no longer supported but SquashFS is. | ||
51 | 82 | # Since the DI won't be used don't generate them. | ||
52 | 83 | self.enable_di = False | ||
53 | 84 | else: | ||
54 | 85 | self.enable_di = self.config.get('enable_di', True) | ||
55 | 78 | 86 | ||
56 | 79 | with open(v2config) as fp: | 87 | with open(v2config) as fp: |
57 | 80 | cfgdata = yaml.load(fp) | 88 | cfgdata = yaml.load(fp) |
58 | @@ -117,6 +125,25 @@ | |||
59 | 117 | self.content_t = my_prods | 125 | self.content_t = my_prods |
60 | 118 | return v2_to_cloudimg_products(my_prods, rebuilds=self.rebuilds) | 126 | return v2_to_cloudimg_products(my_prods, rebuilds=self.rebuilds) |
61 | 119 | 127 | ||
62 | 128 | def _verify_sha256(self, filename, expected_sha256): | ||
63 | 129 | sha256 = hashlib.sha256() | ||
64 | 130 | with open(filename, 'rb') as f: | ||
65 | 131 | while True: | ||
66 | 132 | data = f.read(2**18) | ||
67 | 133 | if not data: | ||
68 | 134 | break | ||
69 | 135 | sha256.update(data) | ||
70 | 136 | if sha256.hexdigest() != expected_sha256: | ||
71 | 137 | raise ValueError( | ||
72 | 138 | 'Expected SHA256 %s got %s on %s' % | ||
73 | 139 | (expected_sha256, sha256.hexdigest(), filename)) | ||
74 | 140 | |||
75 | 141 | def _remove_unused(self, filename): | ||
76 | 142 | """Remove the specified file if a squashfs image exists.""" | ||
77 | 143 | squashfs_image = os.path.join(os.path.dirname(filename), '*.squashfs') | ||
78 | 144 | if len(glob.glob(squashfs_image)) > 0 and os.path.exists(filename): | ||
79 | 145 | os.remove(filename) | ||
80 | 146 | |||
81 | 120 | def insert_item(self, data, src, target, pedigree, contentsource): | 147 | def insert_item(self, data, src, target, pedigree, contentsource): |
82 | 121 | # create the ephemeral root | 148 | # create the ephemeral root |
83 | 122 | 149 | ||
84 | @@ -138,8 +165,37 @@ | |||
85 | 138 | 165 | ||
86 | 139 | for prodname, items in cvret.items(): | 166 | for prodname, items in cvret.items(): |
87 | 140 | for i in items: | 167 | for i in items: |
90 | 141 | sutil.products_set(self.content_t, items[i], | 168 | filename = os.path.join(self.out_d, items[i]['path']) |
91 | 142 | (prodname, vername, i)) | 169 | if 'squashfs' in i and not self.squashfs: |
92 | 170 | # If we're not publishing the SquashFS image but one | ||
93 | 171 | # was used to generate root-image.gz delete it. | ||
94 | 172 | os.remove(filename) | ||
95 | 173 | continue | ||
96 | 174 | elif i == 'root-image.gz' and self.squashfs: | ||
97 | 175 | # If we're publishing the SquashFS image we don't need the | ||
98 | 176 | # root-image after its been used to generate kernels | ||
99 | 177 | self._remove_unused(filename) | ||
100 | 178 | continue | ||
101 | 179 | elif i == 'manifest' and self.squashfs: | ||
102 | 180 | # If we're publishing the SquashFS image we don't need the | ||
103 | 181 | # root-image manifest either. | ||
104 | 182 | self._remove_unused(filename) | ||
105 | 183 | continue | ||
106 | 184 | if i == 'squashfs': | ||
107 | 185 | # Verify upstream SHA256 of SquashFS images and add | ||
108 | 186 | # SHA256 and size to our stream. | ||
109 | 187 | self._verify_sha256(filename, flat['sha256']) | ||
110 | 188 | items[i]['sha256'] = flat['sha256'] | ||
111 | 189 | items[i]['size'] = int(flat['size']) | ||
112 | 190 | elif i == 'squashfs.manifest': | ||
113 | 191 | manifest_pedigree = pedigree[:2] + ('squashfs.manifest',) | ||
114 | 192 | manifest_flat = sutil.products_exdata( | ||
115 | 193 | src, manifest_pedigree) | ||
116 | 194 | self._verify_sha256(filename, manifest_flat['sha256']) | ||
117 | 195 | items[i]['sha256'] = manifest_flat['sha256'] | ||
118 | 196 | items[i]['size'] = int(manifest_flat['size']) | ||
119 | 197 | sutil.products_set( | ||
120 | 198 | self.content_t, items[i], (prodname, vername, i)) | ||
121 | 143 | 199 | ||
122 | 144 | def insert_products(self, path, target, content): | 200 | def insert_products(self, path, target, content): |
123 | 145 | tree = copy.deepcopy(self.content_t) | 201 | tree = copy.deepcopy(self.content_t) |
124 | @@ -184,7 +240,12 @@ | |||
125 | 184 | return True | 240 | return True |
126 | 185 | 241 | ||
127 | 186 | def filter_item(self, data, src, target, pedigree): | 242 | def filter_item(self, data, src, target, pedigree): |
129 | 187 | if data['ftype'] != "tar.gz": | 243 | # Only use tar.gz if no SquashFS image is available |
130 | 244 | if data['ftype'] == 'tar.gz': | ||
131 | 245 | product = src['products'][pedigree[0]]['versions'][pedigree[1]] | ||
132 | 246 | if 'squashfs' in product['items'].keys(): | ||
133 | 247 | return False | ||
134 | 248 | elif data['ftype'] != 'squashfs': | ||
135 | 188 | return False | 249 | return False |
136 | 189 | return filters.filter_item(self.filters, data, src, pedigree) | 250 | return filters.filter_item(self.filters, data, src, pedigree) |
137 | 190 | 251 | ||
138 | @@ -212,6 +273,9 @@ | |||
139 | 212 | parser.add_argument('--verbose', '-v', action='count', default=0) | 273 | parser.add_argument('--verbose', '-v', action='count', default=0) |
140 | 213 | parser.add_argument('--log-file', default=sys.stderr, | 274 | parser.add_argument('--log-file', default=sys.stderr, |
141 | 214 | type=argparse.FileType('w')) | 275 | type=argparse.FileType('w')) |
142 | 276 | parser.add_argument('--squashfs', action='store_true', default=False, | ||
143 | 277 | help='Download SquashFS root file systems if available' | ||
144 | 278 | ) | ||
145 | 215 | 279 | ||
146 | 216 | parser.add_argument('output_d') | 280 | parser.add_argument('output_d') |
147 | 217 | parser.add_argument('filters', nargs='*', default=[]) | 281 | parser.add_argument('filters', nargs='*', default=[]) |
148 | @@ -272,7 +336,8 @@ | |||
149 | 272 | 336 | ||
150 | 273 | tmirror = CloudImg2Meph2Sync(config=mirror_config, out_d=args.output_d, | 337 | tmirror = CloudImg2Meph2Sync(config=mirror_config, out_d=args.output_d, |
151 | 274 | target=args.target, v2config=args.config, | 338 | target=args.target, v2config=args.config, |
153 | 275 | rebuilds=rebuilds, verbosity=vlevel) | 339 | rebuilds=rebuilds, verbosity=vlevel, |
154 | 340 | squashfs=args.squashfs) | ||
155 | 276 | 341 | ||
156 | 277 | tmirror.sync(smirror, initial_path) | 342 | tmirror.sync(smirror, initial_path) |
157 | 278 | 343 | ||
158 | 279 | 344 | ||
159 | === modified file 'meph2/commands/meph2_util.py' | |||
160 | --- meph2/commands/meph2_util.py 2016-07-29 22:28:22 +0000 | |||
161 | +++ meph2/commands/meph2_util.py 2016-08-01 19:18:07 +0000 | |||
162 | @@ -629,7 +629,7 @@ | |||
163 | 629 | cfg_path = args.import_cfg | 629 | cfg_path = args.import_cfg |
164 | 630 | else: | 630 | else: |
165 | 631 | print("Error: Unable to find config file %s" % args.import_cfg) | 631 | print("Error: Unable to find config file %s" % args.import_cfg) |
167 | 632 | os.exit(1) | 632 | sys.exit(1) |
168 | 633 | 633 | ||
169 | 634 | with open(cfg_path) as fp: | 634 | with open(cfg_path) as fp: |
170 | 635 | cfgdata = yaml.load(fp) | 635 | cfgdata = yaml.load(fp) |
171 | 636 | 636 | ||
172 | === modified file 'meph2/stream.py' | |||
173 | --- meph2/stream.py 2016-05-11 18:41:21 +0000 | |||
174 | +++ meph2/stream.py 2016-08-01 19:18:07 +0000 | |||
175 | @@ -20,6 +20,10 @@ | |||
176 | 20 | PATH_FORMATS = { | 20 | PATH_FORMATS = { |
177 | 21 | 'root-image.gz': PATH_COMMON + "%(version_name)s/root-image.gz", | 21 | 'root-image.gz': PATH_COMMON + "%(version_name)s/root-image.gz", |
178 | 22 | 'manifest': PATH_COMMON + "%(version_name)s/root-image.manifest", | 22 | 'manifest': PATH_COMMON + "%(version_name)s/root-image.manifest", |
179 | 23 | 'squashfs': PATH_COMMON + "%(version_name)s/%(img_name)s", | ||
180 | 24 | 'squashfs.manifest': ( | ||
181 | 25 | PATH_COMMON + "%(version_name)s/%(img_name)s.manifest" | ||
182 | 26 | ), | ||
183 | 23 | 'boot-dtb': BOOT_COMMON + "/boot-dtb%(suffix)s", | 27 | 'boot-dtb': BOOT_COMMON + "/boot-dtb%(suffix)s", |
184 | 24 | 'boot-kernel': BOOT_COMMON + "/boot-kernel%(suffix)s", | 28 | 'boot-kernel': BOOT_COMMON + "/boot-kernel%(suffix)s", |
185 | 25 | 'boot-initrd': BOOT_COMMON + "/boot-initrd%(suffix)s", | 29 | 'boot-initrd': BOOT_COMMON + "/boot-initrd%(suffix)s", |
186 | @@ -116,7 +120,8 @@ | |||
187 | 116 | newitems = {} | 120 | newitems = {} |
188 | 117 | 121 | ||
189 | 118 | subs = {'release': release, 'arch': arch, | 122 | subs = {'release': release, 'arch': arch, |
191 | 119 | 'version_name': version_name, 'version': version} | 123 | 'version_name': version_name, 'version': version, |
192 | 124 | 'img_name': os.path.basename(img_url)} | ||
193 | 120 | 125 | ||
194 | 121 | rootimg_path = PATH_FORMATS['root-image.gz'] % subs | 126 | rootimg_path = PATH_FORMATS['root-image.gz'] % subs |
195 | 122 | manifest_path = PATH_FORMATS['manifest'] % subs | 127 | manifest_path = PATH_FORMATS['manifest'] % subs |
196 | @@ -151,6 +156,8 @@ | |||
197 | 151 | 156 | ||
198 | 152 | boot_keys = ['boot-kernel', 'boot-initrd'] | 157 | boot_keys = ['boot-kernel', 'boot-initrd'] |
199 | 153 | ikeys = boot_keys + ['root-image.gz', 'manifest'] | 158 | ikeys = boot_keys + ['root-image.gz', 'manifest'] |
200 | 159 | if img_url.endswith('.squashfs'): | ||
201 | 160 | ikeys += ['squashfs'] | ||
202 | 154 | 161 | ||
203 | 155 | dtb = kdata.get('dtb') | 162 | dtb = kdata.get('dtb') |
204 | 156 | if dtb: | 163 | if dtb: |
Why do we need to create a root-image.gz anymore ? I don't think we want to create any images anymore. If there's a squashfs image, then we should use that and not have to create more and more and more stuff.
We are trying to reduce the amount of images and what we have available and what we use, creating a root-image.gz adds more stuff that we dont really need?