Merge lp:~ltrager/maas/new_kernels into lp:~maas-committers/maas/trunk
- new_kernels
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Lee Trager | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 5313 | ||||
Proposed branch: | lp:~ltrager/maas/new_kernels | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
1489 lines (+746/-141) 25 files modified
src/maasserver/bootresources.py (+8/-6) src/maasserver/bootsources.py (+6/-1) src/maasserver/migrations/builtin/maasserver/0081_allow_larger_bootsourcecache_fields.py (+42/-0) src/maasserver/migrations/builtin/maasserver/0082_add_kflavor.py (+47/-0) src/maasserver/models/bootresource.py (+24/-5) src/maasserver/models/bootsourcecache.py (+17/-9) src/maasserver/models/tests/test_bootresource.py (+23/-2) src/maasserver/rpc/tests/test_regionservice_calls.py (+1/-2) src/maasserver/static/js/angular/controllers/node_details.js (+1/-1) src/maasserver/static/js/angular/controllers/tests/test_node_details.js (+2/-2) src/maasserver/static/js/angular/factories/general.js (+7/-0) src/maasserver/static/js/angular/factories/tests/test_general.js (+6/-3) src/maasserver/testing/architecture.py (+1/-0) src/maasserver/testing/factory.py (+27/-7) src/maasserver/testing/osystems.py (+5/-0) src/maasserver/tests/test_bootresources.py (+27/-6) src/maasserver/utils/osystems.py (+163/-40) src/maasserver/utils/tests/test_osystems.py (+206/-18) src/maasserver/websockets/handlers/general.py (+11/-0) src/maasserver/websockets/handlers/tests/test_general.py (+49/-30) src/maastesting/factory.py (+26/-0) src/provisioningserver/import_images/download_descriptions.py (+9/-7) src/provisioningserver/import_images/tests/test_boot_resources.py (+1/-0) src/provisioningserver/import_images/tests/test_download_descriptions.py (+36/-2) utilities/check-imports (+1/-0) |
||||
To merge this branch: | bzr merge lp:~ltrager/maas/new_kernels | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Andres Rodriguez (community) | Needs Information | ||
Review via email: mp+303506@code.launchpad.net |
Commit message
Add support for new style kernels(e.g hwe-16.04), lowlatency kernels, rolling kernels, and edge kernels. While the old method was able to use an algorithm based on the first letter of the release this looks up the release/version mapping using the UbuntuDistro library. This will require the region to be running on a system with an updated distro-info-data package, otherwise new releases added to the stream won't be deployable.
The new kernel name hwe-16.
Description of the change
This adds support for new style kernels(e.g hwe-16.04), lowlatency kernels, rolling kernels, and edge kernels. Old style kernels(e.g hwe-t) are still supported and can be used along side new style kernels.
Scott Moser (smoser) : | # |
Andres Rodriguez (andreserl) : | # |
Lee Trager (ltrager) wrote : | # |
I've updated the branch to include the kflavor in the BootResource and BootSourceCache tables. The kflavor field is now used instead of accessing extra['kflavor'] throughout the region. The rack currently broadly assumes the subarch is the kernel. From how the rack reads the SimpleStream created by the region, to how kernels are stored on the filesystem, and the TGT and GRUB config files are generated. If we make that much change it brings up questions as to how we're presenting hwe_kernels and flavors to the user as well.
I've also allowed any kernel type to be used as the min_hwe_kernel. If a min_hwe_kernel is set and the user does not set a hwe_kernel during deploy MAAS picks a kernel at or above the min_hwe_kernel with the same kflavor.
Andres Rodriguez (andreserl) wrote : | # |
I have one question, how will this change affect the directories kernels are placed on the system in the Rack Controller?
The real question I want to answer is:
If we are to upgrade from 2.0 to 2.1 and the user doesn't update their images/kernels (or doesn't update the streams to the new version), will MAAS be able to continue to deploy machines with the current images/kernels available already in the FS?
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~ltrager/maas/new_kernels into lp:maas failed. Below is the output from the failed tests.
Hit:1 http://
Get:2 http://
Get:3 http://
Hit:4 http://
Fetched 190 kB in 0s (451 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
avahi-utils is already the newest version (0.6.32~
build-essential is already the newest version (12.1ubuntu2).
debhelper is already the newest version (9.20160115ubun
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
make is already the newest version (4.1-6).
postgresql is already the newest version (9.5+173).
pxelinux is already the newest version (3:6.03+
python-formencode is already the newest version (1.3.0-0ubuntu5).
python-lxml is already the newest version (3.5.0-1build1).
python-netaddr is already the newest ve...
Preview Diff
1 | === modified file 'src/maasserver/bootresources.py' |
2 | --- src/maasserver/bootresources.py 2016-08-19 10:22:29 +0000 |
3 | +++ src/maasserver/bootresources.py 2016-08-31 06:33:18 +0000 |
4 | @@ -458,8 +458,12 @@ |
5 | os = get_os_from_product(product) |
6 | series = product['release'] |
7 | arch = product['arch'] |
8 | - subarch = product['subarch'] |
9 | name = '%s/%s' % (os, series) |
10 | + if 'kflavor' in product and product['kflavor'] != 'generic': |
11 | + subarch = "%s-%s" % (product['subarch'], product['kflavor']) |
12 | + else: |
13 | + subarch = product['subarch'] |
14 | + |
15 | architecture = '%s/%s' % (arch, subarch) |
16 | |
17 | # Allow a generated resource to be replaced by a sycned resource. This |
18 | @@ -488,16 +492,14 @@ |
19 | # replaced with this synced image. |
20 | resource.rtype = BOOT_RESOURCE_TYPE.SYNCED |
21 | |
22 | + resource.kflavor = product.get('kflavor') |
23 | # Simplestreams content from maas.io includes the following |
24 | # extra fields. Looping through the extra product data and adding it to |
25 | # extra will not work as the product data that is passed into this |
26 | # object store contains additional data that should not be stored into |
27 | - # the database. If kflavor and/or subarches exist in the product then |
28 | - # we store those values to expose in the simplestreams endpoint on the |
29 | - # region. |
30 | + # the database. If subarches exist in the product then we store those |
31 | + # values to expose in the simplestreams endpoint on the region. |
32 | resource.extra = {} |
33 | - if 'kflavor' in product: |
34 | - resource.extra['kflavor'] = product['kflavor'] |
35 | if 'subarches' in product: |
36 | resource.extra['subarches'] = product['subarches'] |
37 | |
38 | |
39 | === modified file 'src/maasserver/bootsources.py' |
40 | --- src/maasserver/bootsources.py 2016-06-16 20:51:55 +0000 |
41 | +++ src/maasserver/bootsources.py 2016-08-31 06:33:18 +0000 |
42 | @@ -165,9 +165,14 @@ |
43 | BootSourceCache.objects.filter(boot_source=bootsource).delete() |
44 | if not descriptions.is_empty(): |
45 | for spec, item in descriptions.mapping.items(): |
46 | + kflavor = item.get('kflavor') |
47 | + if kflavor not in (None, 'generic'): |
48 | + subarch = "%s-%s" % (spec.subarch, kflavor) |
49 | + else: |
50 | + subarch = spec.subarch |
51 | BootSourceCache.objects.create( |
52 | boot_source=bootsource, os=spec.os, |
53 | - arch=spec.arch, subarch=spec.subarch, |
54 | + arch=spec.arch, subarch=subarch, kflavor=kflavor, |
55 | release=spec.release, label=spec.label, |
56 | release_codename=item.get('release_codename'), |
57 | release_title=item.get('release_title'), |
58 | |
59 | === added file 'src/maasserver/migrations/builtin/maasserver/0081_allow_larger_bootsourcecache_fields.py' |
60 | --- src/maasserver/migrations/builtin/maasserver/0081_allow_larger_bootsourcecache_fields.py 1970-01-01 00:00:00 +0000 |
61 | +++ src/maasserver/migrations/builtin/maasserver/0081_allow_larger_bootsourcecache_fields.py 2016-08-31 06:33:18 +0000 |
62 | @@ -0,0 +1,42 @@ |
63 | +# -*- coding: utf-8 -*- |
64 | +from __future__ import unicode_literals |
65 | + |
66 | +from django.db import ( |
67 | + migrations, |
68 | + models, |
69 | +) |
70 | + |
71 | + |
72 | +class Migration(migrations.Migration): |
73 | + |
74 | + dependencies = [ |
75 | + ('maasserver', '0080_change_packagerepository_url_type'), |
76 | + ] |
77 | + |
78 | + operations = [ |
79 | + migrations.AlterField( |
80 | + model_name='bootsourcecache', |
81 | + name='arch', |
82 | + field=models.CharField(max_length=32), |
83 | + ), |
84 | + migrations.AlterField( |
85 | + model_name='bootsourcecache', |
86 | + name='label', |
87 | + field=models.CharField(max_length=32), |
88 | + ), |
89 | + migrations.AlterField( |
90 | + model_name='bootsourcecache', |
91 | + name='os', |
92 | + field=models.CharField(max_length=32), |
93 | + ), |
94 | + migrations.AlterField( |
95 | + model_name='bootsourcecache', |
96 | + name='release', |
97 | + field=models.CharField(max_length=32), |
98 | + ), |
99 | + migrations.AlterField( |
100 | + model_name='bootsourcecache', |
101 | + name='subarch', |
102 | + field=models.CharField(max_length=32), |
103 | + ), |
104 | + ] |
105 | |
106 | === added file 'src/maasserver/migrations/builtin/maasserver/0082_add_kflavor.py' |
107 | --- src/maasserver/migrations/builtin/maasserver/0082_add_kflavor.py 1970-01-01 00:00:00 +0000 |
108 | +++ src/maasserver/migrations/builtin/maasserver/0082_add_kflavor.py 2016-08-31 06:33:18 +0000 |
109 | @@ -0,0 +1,47 @@ |
110 | +# -*- coding: utf-8 -*- |
111 | +from __future__ import unicode_literals |
112 | + |
113 | +from django.db import ( |
114 | + migrations, |
115 | + models, |
116 | +) |
117 | + |
118 | + |
119 | +def add_kflavor_to_boot_resource(apps, schema_editor): |
120 | + BootResource = apps.get_model('maasserver', 'BootResource') |
121 | + for resource in BootResource.objects.all(): |
122 | + if 'kflavor' in resource.extra: |
123 | + resource.kflavor = resource.extra['kflavor'] |
124 | + del resource.extra['kflavor'] |
125 | + resource.save() |
126 | + |
127 | + |
128 | +def add_kflavor_to_boot_source_cache(apps, schema_editor): |
129 | + BootSourceCache = apps.get_model('maasserver', 'BootSourceCache') |
130 | + for bsc in BootSourceCache.objects.filter(os='ubuntu'): |
131 | + # The kflavor was never stored as part of the BootSourceCache |
132 | + # however previosuly we only had generic kernels in the stream. |
133 | + bsc.kflavor = 'generic' |
134 | + bsc.save() |
135 | + |
136 | + |
137 | +class Migration(migrations.Migration): |
138 | + |
139 | + dependencies = [ |
140 | + ('maasserver', '0081_allow_larger_bootsourcecache_fields'), |
141 | + ] |
142 | + |
143 | + operations = [ |
144 | + migrations.AddField( |
145 | + model_name='bootresource', |
146 | + name='kflavor', |
147 | + field=models.CharField(max_length=32, blank=True, null=True), |
148 | + ), |
149 | + migrations.RunPython(add_kflavor_to_boot_resource), |
150 | + migrations.AddField( |
151 | + model_name='bootsourcecache', |
152 | + name='kflavor', |
153 | + field=models.CharField(max_length=32, blank=True, null=True), |
154 | + ), |
155 | + migrations.RunPython(add_kflavor_to_boot_source_cache), |
156 | + ] |
157 | |
158 | === modified file 'src/maasserver/models/bootresource.py' |
159 | --- src/maasserver/models/bootresource.py 2016-07-30 01:17:54 +0000 |
160 | +++ src/maasserver/models/bootresource.py 2016-08-31 06:33:18 +0000 |
161 | @@ -220,7 +220,8 @@ |
162 | return False |
163 | return True |
164 | |
165 | - def get_usable_hwe_kernels(self, name=None, architecture=None): |
166 | + def get_usable_hwe_kernels( |
167 | + self, name=None, architecture=None, kflavor=None): |
168 | """Return the set of usable kernels for architecture and release.""" |
169 | if not name: |
170 | name = '' |
171 | @@ -229,6 +230,8 @@ |
172 | kernels = set() |
173 | for resource in self.filter( |
174 | architecture__startswith=architecture, name__startswith=name): |
175 | + if kflavor is not None and resource.kflavor != kflavor: |
176 | + continue |
177 | resource_set = resource.get_latest_set() |
178 | if(resource_set is None or |
179 | not resource_set.commissionable or |
180 | @@ -240,8 +243,22 @@ |
181 | if "subarches" in resource.extra: |
182 | for subarch in resource.extra["subarches"].split(","): |
183 | if subarch.startswith("hwe-"): |
184 | - kernels.add(subarch) |
185 | - return sorted(kernels) |
186 | + if kflavor is None: |
187 | + kernels.add(subarch) |
188 | + else: |
189 | + # generic kflavors are not included in the subarch. |
190 | + if kflavor == 'generic': |
191 | + kparts = subarch.split('-') |
192 | + if len(kparts) == 2: |
193 | + kernels.add(subarch) |
194 | + else: |
195 | + if kflavor in subarch: |
196 | + kernels.add(subarch) |
197 | + # Make sure kernels named with a version come after the kernels named |
198 | + # with the first letter of release. This switched in Xenial so this |
199 | + # preserves the chronological order of the kernels. |
200 | + return sorted( |
201 | + kernels, key=lambda k: (k.replace('hwe-', '')[0].isdigit(), k)) |
202 | |
203 | def get_kpackage_for_node(self, node): |
204 | """Return the kernel package name for the kernel specified.""" |
205 | @@ -340,11 +357,13 @@ |
206 | architecture = CharField( |
207 | max_length=255, blank=False, validators=[validate_architecture]) |
208 | |
209 | + kflavor = CharField(max_length=32, blank=True, null=True) |
210 | + |
211 | extra = JSONObjectField(blank=True, default="", editable=False) |
212 | |
213 | def __str__(self): |
214 | - return "<BootResource name=%s, arch=%s>" % ( |
215 | - self.name, self.architecture) |
216 | + return "<BootResource name=%s, arch=%s, kflavor=%s>" % ( |
217 | + self.name, self.architecture, self.kflavor) |
218 | |
219 | @property |
220 | def display_rtype(self): |
221 | |
222 | === modified file 'src/maasserver/models/bootsourcecache.py' |
223 | --- src/maasserver/models/bootsourcecache.py 2016-06-22 17:03:02 +0000 |
224 | +++ src/maasserver/models/bootsourcecache.py 2016-08-31 06:33:18 +0000 |
225 | @@ -51,18 +51,26 @@ |
226 | |
227 | boot_source = ForeignKey(BootSource, blank=False) |
228 | |
229 | - os = CharField(max_length=20, blank=False, null=False) |
230 | - |
231 | - arch = CharField(max_length=20, blank=False, null=False) |
232 | - |
233 | - subarch = CharField(max_length=20, blank=False, null=False) |
234 | - |
235 | - release = CharField(max_length=20, blank=False, null=False) |
236 | - |
237 | - label = CharField(max_length=20, blank=False, null=False) |
238 | + os = CharField(max_length=32, blank=False, null=False) |
239 | + |
240 | + arch = CharField(max_length=32, blank=False, null=False) |
241 | + |
242 | + subarch = CharField(max_length=32, blank=False, null=False) |
243 | + |
244 | + kflavor = CharField(max_length=32, blank=True, null=True) |
245 | + |
246 | + release = CharField(max_length=32, blank=False, null=False) |
247 | + |
248 | + label = CharField(max_length=32, blank=False, null=False) |
249 | |
250 | release_codename = CharField(max_length=255, blank=True, null=True) |
251 | |
252 | release_title = CharField(max_length=255, blank=True, null=True) |
253 | |
254 | support_eol = DateField(null=True, blank=True) |
255 | + |
256 | + def __str__(self): |
257 | + return ( |
258 | + "<BootSourceCache os=%s, release=%s, arch=%s, subarch=%s, " |
259 | + "kflavor=%s>" % ( |
260 | + self.os, self.release, self.arch, self.subarch, self.kflavor)) |
261 | |
262 | === modified file 'src/maasserver/models/tests/test_bootresource.py' |
263 | --- src/maasserver/models/tests/test_bootresource.py 2016-05-25 21:03:50 +0000 |
264 | +++ src/maasserver/models/tests/test_bootresource.py 2016-08-31 06:33:18 +0000 |
265 | @@ -594,15 +594,30 @@ |
266 | "arch": "armfh", |
267 | "subarch": "hardbank", |
268 | "kernels": [], |
269 | - })) |
270 | + }), |
271 | + ("ubuntu/xenial", { |
272 | + "name": "ubuntu/xenial", |
273 | + "arch": "amd64", |
274 | + "subarch": "generic", |
275 | + "kernels": ["hwe-16.04", "hwe-16.04-lowlatency"], |
276 | + }), |
277 | + ) |
278 | |
279 | def test__returns_usable_kernels(self): |
280 | if self.subarch == "generic": |
281 | + generic_kernels = [] |
282 | for i in self.kernels: |
283 | + kernel_parts = i.split("-") |
284 | + if len(kernel_parts) > 2: |
285 | + kflavor = kernel_parts[2] |
286 | + else: |
287 | + kflavor = "generic" |
288 | + generic_kernels.append(i) |
289 | factory.make_usable_boot_resource( |
290 | name=self.name, rtype=BOOT_RESOURCE_TYPE.SYNCED, |
291 | - architecture="%s/%s" % (self.arch, i)) |
292 | + architecture="%s/%s" % (self.arch, i), kflavor=kflavor) |
293 | else: |
294 | + generic_kernels = self.kernels |
295 | factory.make_usable_boot_resource( |
296 | name=self.name, rtype=BOOT_RESOURCE_TYPE.SYNCED, |
297 | architecture="%s/%s" % (self.arch, self.subarch)) |
298 | @@ -612,6 +627,12 @@ |
299 | self.name, self.arch), |
300 | "%s should return %s as its usable kernel" % ( |
301 | self.name, self.kernels)) |
302 | + self.assertEqual( |
303 | + generic_kernels, |
304 | + BootResource.objects.get_usable_hwe_kernels( |
305 | + self.name, self.arch, 'generic'), |
306 | + "%s should return %s as its usable kernel" % ( |
307 | + self.name, generic_kernels)) |
308 | |
309 | |
310 | class TestGetKpackageForNode(MAASServerTestCase): |
311 | |
312 | === modified file 'src/maasserver/rpc/tests/test_regionservice_calls.py' |
313 | --- src/maasserver/rpc/tests/test_regionservice_calls.py 2016-08-19 18:11:11 +0000 |
314 | +++ src/maasserver/rpc/tests/test_regionservice_calls.py 2016-08-31 06:33:18 +0000 |
315 | @@ -47,7 +47,6 @@ |
316 | from maasserver.rpc.regionservice import Region |
317 | from maasserver.rpc.services import update_services |
318 | from maasserver.security import get_shared_secret |
319 | -from maasserver.testing.architecture import make_usable_architecture |
320 | from maasserver.testing.eventloop import RegionEventLoopFixture |
321 | from maasserver.testing.factory import factory |
322 | from maasserver.testing.testcase import MAASTransactionServerTestCase |
323 | @@ -1104,7 +1103,7 @@ |
324 | self.create_node) |
325 | |
326 | params = { |
327 | - 'architecture': make_usable_architecture(self), |
328 | + 'architecture': factory.make_name('arch'), |
329 | 'power_type': factory.make_name('power_type'), |
330 | 'power_parameters': dumps({}), |
331 | 'mac_addresses': [factory.make_mac_address()], |
332 | |
333 | === modified file 'src/maasserver/static/js/angular/controllers/node_details.js' |
334 | --- src/maasserver/static/js/angular/controllers/node_details.js 2016-07-15 00:42:25 +0000 |
335 | +++ src/maasserver/static/js/angular/controllers/node_details.js 2016-08-31 06:33:18 +0000 |
336 | @@ -62,7 +62,7 @@ |
337 | }, |
338 | min_hwe_kernel: { |
339 | selected: null, |
340 | - options: GeneralManager.getData("hwe_kernels") |
341 | + options: GeneralManager.getData("min_hwe_kernels") |
342 | }, |
343 | zone: { |
344 | selected: null, |
345 | |
346 | === modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details.js' |
347 | --- src/maasserver/static/js/angular/controllers/tests/test_node_details.js 2016-07-15 18:00:04 +0000 |
348 | +++ src/maasserver/static/js/angular/controllers/tests/test_node_details.js 2016-08-31 06:33:18 +0000 |
349 | @@ -212,7 +212,7 @@ |
350 | }, |
351 | min_hwe_kernel: { |
352 | selected: null, |
353 | - options: GeneralManager.getData("hwe_kernels") |
354 | + options: GeneralManager.getData("min_hwe_kernels") |
355 | }, |
356 | zone: { |
357 | selected: null, |
358 | @@ -223,7 +223,7 @@ |
359 | expect($scope.summary.architecture.options).toBe( |
360 | GeneralManager.getData("architectures")); |
361 | expect($scope.summary.min_hwe_kernel.options).toBe( |
362 | - GeneralManager.getData("hwe_kernels")); |
363 | + GeneralManager.getData("min_hwe_kernels")); |
364 | expect($scope.summary.zone.options).toBe( |
365 | ZonesManager.getItems()); |
366 | }); |
367 | |
368 | === modified file 'src/maasserver/static/js/angular/factories/general.js' |
369 | --- src/maasserver/static/js/angular/factories/general.js 2016-08-18 15:42:50 +0000 |
370 | +++ src/maasserver/static/js/angular/factories/general.js 2016-08-31 06:33:18 +0000 |
371 | @@ -84,6 +84,13 @@ |
372 | polling: false, |
373 | nextPromise: null |
374 | }, |
375 | + min_hwe_kernels: { |
376 | + method: "general.min_hwe_kernels", |
377 | + data: [], |
378 | + loaded: false, |
379 | + polling: false, |
380 | + nextPromise: null |
381 | + }, |
382 | default_min_hwe_kernel: { |
383 | method: "general.default_min_hwe_kernel", |
384 | data: { text: '' }, |
385 | |
386 | === modified file 'src/maasserver/static/js/angular/factories/tests/test_general.js' |
387 | --- src/maasserver/static/js/angular/factories/tests/test_general.js 2016-08-19 13:29:03 +0000 |
388 | +++ src/maasserver/static/js/angular/factories/tests/test_general.js 2016-08-31 06:33:18 +0000 |
389 | @@ -53,8 +53,9 @@ |
390 | ["machine_actions", "device_actions", "region_controller_actions", |
391 | "rack_controller_actions", "region_and_rack_controller_actions", |
392 | "architectures", "known_architectures", "pockets_to_disable", |
393 | - "hwe_kernels", "default_min_hwe_kernel", "osinfo", "bond_options", |
394 | - "version", "power_types", "release_options"]); |
395 | + "hwe_kernels", "min_hwe_kernels", "default_min_hwe_kernel", |
396 | + "osinfo", "bond_options", "version", "power_types", |
397 | + "release_options"]); |
398 | }); |
399 | |
400 | it("_data.machine_actions has correct data", function() { |
401 | @@ -321,6 +322,7 @@ |
402 | GeneralManager._data.known_architectures.loaded = true; |
403 | GeneralManager._data.pockets_to_disable.loaded = true; |
404 | GeneralManager._data.hwe_kernels.loaded = true; |
405 | + GeneralManager._data.min_hwe_kernels.loaded = true; |
406 | GeneralManager._data.default_min_hwe_kernel.loaded = true; |
407 | GeneralManager._data.osinfo.loaded = true; |
408 | GeneralManager._data.bond_options.loaded = true; |
409 | @@ -584,7 +586,7 @@ |
410 | spyOn(GeneralManager, "_loadData").and.returnValue( |
411 | $q.defer().promise); |
412 | GeneralManager.loadItems(); |
413 | - expect(GeneralManager._loadData.calls.count()).toBe(15); |
414 | + expect(GeneralManager._loadData.calls.count()).toBe(16); |
415 | }); |
416 | |
417 | it("resolve defer once all resolve", function(done) { |
418 | @@ -603,6 +605,7 @@ |
419 | $q.defer(), |
420 | $q.defer(), |
421 | $q.defer(), |
422 | + $q.defer(), |
423 | $q.defer() |
424 | ]; |
425 | var i = 0; |
426 | |
427 | === modified file 'src/maasserver/testing/architecture.py' |
428 | --- src/maasserver/testing/architecture.py 2016-03-28 13:54:47 +0000 |
429 | +++ src/maasserver/testing/architecture.py 2016-08-31 06:33:18 +0000 |
430 | @@ -22,6 +22,7 @@ |
431 | """ |
432 | if arch_name is None: |
433 | arch_name = factory.make_name('arch') |
434 | + factory.make_default_ubuntu_release_bootable(arch_name) |
435 | if with_subarch: |
436 | if subarch_name is None: |
437 | subarch_name = factory.make_name('sub') |
438 | |
439 | === modified file 'src/maasserver/testing/factory.py' |
440 | --- src/maasserver/testing/factory.py 2016-08-26 08:04:38 +0000 |
441 | +++ src/maasserver/testing/factory.py 2016-08-31 06:33:18 +0000 |
442 | @@ -22,6 +22,7 @@ |
443 | from distro_info import UbuntuDistroInfo |
444 | from django.conf import settings |
445 | from django.contrib.auth.models import User |
446 | +from django.db import transaction |
447 | from django.test.client import RequestFactory |
448 | from django.utils import timezone |
449 | from maasserver.clusterrpc.power_parameters import get_power_types |
450 | @@ -56,6 +57,7 @@ |
451 | BootSourceCache, |
452 | BootSourceSelection, |
453 | CacheSet, |
454 | + Config, |
455 | Device, |
456 | DHCPSnippet, |
457 | DNSData, |
458 | @@ -112,6 +114,7 @@ |
459 | from maasserver.testing import get_data |
460 | from maasserver.utils.converters import round_size_to_nearest_block |
461 | from maasserver.utils.orm import reload_object |
462 | +from maasserver.utils.osystems import get_release_from_distro_info |
463 | from maasserver.worker_user import get_worker_user |
464 | import maastesting.factory |
465 | from maastesting.factory import TooManyRandomRetries |
466 | @@ -1272,7 +1275,7 @@ |
467 | def make_BootSourceCache(self, boot_source=None, os=None, arch=None, |
468 | subarch=None, release=None, label=None, |
469 | release_codename=None, release_title=None, |
470 | - support_eol=None): |
471 | + support_eol=None, kflavor=None): |
472 | """Create a new `BootSourceCache`.""" |
473 | if boot_source is None: |
474 | boot_source = self.make_BootSource() |
475 | @@ -1290,7 +1293,7 @@ |
476 | boot_source=boot_source, os=os, arch=arch, |
477 | subarch=subarch, release=release, label=label, |
478 | release_codename=release_codename, release_title=release_title, |
479 | - support_eol=support_eol) |
480 | + support_eol=support_eol, kflavor=kflavor) |
481 | |
482 | def make_many_BootSourceCaches(self, number, **kwargs): |
483 | caches = list() |
484 | @@ -1404,12 +1407,9 @@ |
485 | self.make_name('key'): self.make_name('value') |
486 | for _ in range(3) |
487 | } |
488 | - if kflavor is None: |
489 | - extra['kflavor'] = 'generic' |
490 | - else: |
491 | - extra['kflavor'] = kflavor |
492 | return BootResource.objects.create( |
493 | - rtype=rtype, name=name, architecture=architecture, extra=extra) |
494 | + rtype=rtype, name=name, architecture=architecture, kflavor=kflavor, |
495 | + extra=extra) |
496 | |
497 | def make_BootResourceSet(self, resource, version=None, label=None): |
498 | if version is None: |
499 | @@ -1467,6 +1467,26 @@ |
500 | resource_set, filename=filetype, filetype=filetype) |
501 | return resource |
502 | |
503 | + def make_default_ubuntu_release_bootable(self, arch=None): |
504 | + if arch is None: |
505 | + arch = self.make_name('arch') |
506 | + default_osystem = Config.objects.get_config( |
507 | + name='commissioning_osystem') |
508 | + default_series = Config.objects.get_config( |
509 | + name='commissioning_distro_series') |
510 | + default_name = "%s/%s" % (default_osystem, default_series) |
511 | + release = get_release_from_distro_info(default_series) |
512 | + architecture = "%s/hwe-%s" % (arch, release['version'].split()[0]) |
513 | + try: |
514 | + return BootResource.objects.get( |
515 | + name=default_name, architecture=architecture, |
516 | + rtype=BOOT_RESOURCE_TYPE.SYNCED) |
517 | + except BootResource.DoesNotExist: |
518 | + with transaction.atomic(): |
519 | + return self.make_usable_boot_resource( |
520 | + name=default_name, architecture=architecture, |
521 | + kflavor='generic', rtype=BOOT_RESOURCE_TYPE.SYNCED) |
522 | + |
523 | def make_BlockDevice( |
524 | self, node=None, name=None, id_path=None, size=None, |
525 | block_size=None, tags=None): |
526 | |
527 | === modified file 'src/maasserver/testing/osystems.py' |
528 | --- src/maasserver/testing/osystems.py 2015-12-01 18:12:59 +0000 |
529 | +++ src/maasserver/testing/osystems.py 2016-08-31 06:33:18 +0000 |
530 | @@ -14,6 +14,7 @@ |
531 | make_rpc_osystem, |
532 | make_rpc_release, |
533 | ) |
534 | +from maasserver.models import Node |
535 | from maasserver.testing.factory import factory |
536 | from maasserver.utils import osystems as osystems_module |
537 | |
538 | @@ -34,6 +35,10 @@ |
539 | make_rpc_release(release) |
540 | for release in releases |
541 | ] |
542 | + # If this is being used to test commissioning make sure the default |
543 | + # Ubuntu release is bootable for every known architecture. |
544 | + for node in Node.objects.all(): |
545 | + factory.make_default_ubuntu_release_bootable(node.split_arch()[0]) |
546 | return make_rpc_osystem(osystem_name, releases=rpc_releases) |
547 | |
548 | |
549 | |
550 | === modified file 'src/maasserver/tests/test_bootresources.py' |
551 | --- src/maasserver/tests/test_bootresources.py 2016-08-19 10:22:29 +0000 |
552 | +++ src/maasserver/tests/test_bootresources.py 2016-08-31 06:33:18 +0000 |
553 | @@ -582,11 +582,13 @@ |
554 | AssertConnectionWrapper.connection.connection) |
555 | |
556 | |
557 | -def make_product(ftype=None): |
558 | +def make_product(ftype=None, kflavor=None): |
559 | """Make product dictionary that is just like the one provided |
560 | from simplsetreams.""" |
561 | if ftype is None: |
562 | ftype = factory.pick_choice(BOOT_RESOURCE_FILE_TYPE_CHOICES) |
563 | + if kflavor is None: |
564 | + kflavor = 'generic' |
565 | subarch = factory.make_name('subarch') |
566 | subarches = [factory.make_name('subarch') for _ in range(3)] |
567 | subarches.insert(0, subarch) |
568 | @@ -597,7 +599,7 @@ |
569 | 'arch': factory.make_name('arch'), |
570 | 'subarch': subarch, |
571 | 'release': factory.make_name('release'), |
572 | - 'kflavor': factory.make_name('kflavor'), |
573 | + 'kflavor': kflavor, |
574 | 'subarches': subarches, |
575 | 'version_name': factory.make_name('version'), |
576 | 'label': factory.make_name('label'), |
577 | @@ -607,7 +609,11 @@ |
578 | 'path': '/path/to/%s' % name, |
579 | } |
580 | name = '%s/%s' % (product['os'], product['release']) |
581 | - architecture = '%s/%s' % (product['arch'], product['subarch']) |
582 | + if kflavor == 'generic': |
583 | + subarch = product['subarch'] |
584 | + else: |
585 | + subarch = "%s-%s" % (product['subarch'], kflavor) |
586 | + architecture = '%s/%s' % (product['arch'], subarch) |
587 | return name, architecture, product |
588 | |
589 | |
590 | @@ -697,7 +703,7 @@ |
591 | self.assertEqual(BOOT_RESOURCE_TYPE.SYNCED, resource.rtype) |
592 | self.assertEqual(name, resource.name) |
593 | self.assertEqual(architecture, resource.architecture) |
594 | - self.assertEqual(product['kflavor'], resource.extra['kflavor']) |
595 | + self.assertEqual(product['kflavor'], resource.kflavor) |
596 | self.assertEqual(product['subarches'], resource.extra['subarches']) |
597 | |
598 | def test_get_or_create_boot_resource_gets_resource(self): |
599 | @@ -708,7 +714,7 @@ |
600 | store = BootResourceStore() |
601 | resource = store.get_or_create_boot_resource(product) |
602 | self.assertEqual(expected, resource) |
603 | - self.assertEqual(product['kflavor'], resource.extra['kflavor']) |
604 | + self.assertEqual(product['kflavor'], resource.kflavor) |
605 | self.assertEqual(product['subarches'], resource.extra['subarches']) |
606 | |
607 | def test_get_or_create_boot_resource_calls_prevent_resource_deletion(self): |
608 | @@ -736,6 +742,21 @@ |
609 | self.assertThat( |
610 | mock_prevent, MockNotCalled()) |
611 | |
612 | + def test_get_or_create_boot_resource_adds_kflavor_to_subarch(self): |
613 | + kflavor = factory.make_name('kflavor') |
614 | + _, architecture, product = make_product(kflavor=kflavor) |
615 | + store = BootResourceStore() |
616 | + resource = store.get_or_create_boot_resource(product) |
617 | + self.assertEqual(architecture, reload_object(resource).architecture) |
618 | + |
619 | + def test_get_or_create_boot_resources_add_no_kflavor_for_generic(self): |
620 | + _, architecture, product = make_product(kflavor='generic') |
621 | + store = BootResourceStore() |
622 | + resource = store.get_or_create_boot_resource(product) |
623 | + resource = reload_object(resource) |
624 | + self.assertEqual(architecture, resource.architecture) |
625 | + self.assertNotIn('generic', resource.architecture) |
626 | + |
627 | def test_get_or_create_boot_resource_set_creates_resource_set(self): |
628 | self.useFixture(SignalsDisabled("largefiles")) |
629 | name, architecture, product = make_product() |
630 | @@ -1019,7 +1040,7 @@ |
631 | with transaction.atomic(): |
632 | resource = factory.make_BootResource( |
633 | rtype=BOOT_RESOURCE_TYPE.SYNCED, name=name, |
634 | - architecture=architecture) |
635 | + architecture=architecture, kflavor='generic') |
636 | release_name = resource.name.split('/')[1] |
637 | resource_set = factory.make_BootResourceSet( |
638 | resource, version=product['version_name']) |
639 | |
640 | === modified file 'src/maasserver/utils/osystems.py' |
641 | --- src/maasserver/utils/osystems.py 2016-06-29 09:52:24 +0000 |
642 | +++ src/maasserver/utils/osystems.py 2016-08-31 06:33:18 +0000 |
643 | @@ -5,15 +5,17 @@ |
644 | __all__ = [ |
645 | 'get_distro_series_initial', |
646 | 'get_release_requires_key', |
647 | + 'get_release_version_from_string', |
648 | 'list_all_releases_requiring_keys', |
649 | + 'list_all_usable_hwe_kernels', |
650 | 'list_all_usable_osystems', |
651 | 'list_all_usable_releases', |
652 | - 'list_all_usable_hwe_kernels', |
653 | + 'list_commissioning_choices', |
654 | 'list_hwe_kernel_choices', |
655 | 'list_osystem_choices', |
656 | 'list_release_choices', |
657 | - 'list_commissioning_choices', |
658 | 'make_hwe_kernel_ui_text', |
659 | + 'release_a_newer_than_b', |
660 | 'validate_hwe_kernel', |
661 | ] |
662 | |
663 | @@ -21,6 +23,7 @@ |
664 | |
665 | from distro_info import UbuntuDistroInfo |
666 | from django.core.exceptions import ValidationError |
667 | +from django.db.models import Q |
668 | from maasserver.clusterrpc.osystems import gen_all_known_operating_systems |
669 | from maasserver.models import ( |
670 | BootResource, |
671 | @@ -85,18 +88,20 @@ |
672 | def make_hwe_kernel_ui_text(hwe_kernel): |
673 | if not hwe_kernel: |
674 | return hwe_kernel |
675 | - release_letter = hwe_kernel.replace('hwe-', '') |
676 | - boot_sources = BootSourceCache.objects.filter( |
677 | - release__startswith=release_letter, |
678 | - subarch=hwe_kernel) |
679 | - if len(boot_sources) > 0: |
680 | - return "%s (%s)" % (boot_sources[0].release, hwe_kernel) |
681 | - else: |
682 | - ubuntu = UbuntuDistroInfo() |
683 | - for release in ubuntu.all: |
684 | - if release.startswith(release_letter): |
685 | - return "%s (%s)" % (release, hwe_kernel) |
686 | - return hwe_kernel |
687 | + # Fall back on getting it from DistroInfo. |
688 | + kernel_list = hwe_kernel.split('-') |
689 | + if len(kernel_list) >= 2: |
690 | + kernel = kernel_list[1] |
691 | + else: |
692 | + kernel = hwe_kernel |
693 | + # Try to get the release name from the SimpleStream |
694 | + ubuntu_release = get_release_from_db(kernel) |
695 | + if ubuntu_release is None: |
696 | + ubuntu_release = get_release_from_distro_info(kernel) |
697 | + if ubuntu_release is None: |
698 | + return hwe_kernel |
699 | + else: |
700 | + return "%s (%s)" % (ubuntu_release['series'], hwe_kernel) |
701 | |
702 | |
703 | def list_hwe_kernel_choices(hwe_kernels): |
704 | @@ -248,32 +253,138 @@ |
705 | return osystem, release |
706 | |
707 | |
708 | +def get_release_from_distro_info(string): |
709 | + """Convert an Ubuntu release or version into a release dict. |
710 | + |
711 | + This data is pulled from the UbuntuDistroInfo library which contains |
712 | + additional information such as the release, EOL, and code name.""" |
713 | + ubuntu = UbuntuDistroInfo() |
714 | + release_found = False |
715 | + # We can only look at release names for 12.04+ as previous versions |
716 | + # have overlapping first letters(e.g Warty and Wily) which break looking |
717 | + # up old style kernels(e.g hwe-w). |
718 | + for row in ubuntu._rows: |
719 | + if ( |
720 | + int(row['version'].split('.')[0]) >= 12 and |
721 | + row['series'].startswith(string) or |
722 | + row['version'].startswith(string)): |
723 | + release_found = True |
724 | + break |
725 | + if release_found: |
726 | + return row |
727 | + else: |
728 | + return None |
729 | + |
730 | + |
731 | +def get_release_from_db(string): |
732 | + """Convert an Ubuntu release, version, or subarch into a release dict. |
733 | + |
734 | + This does not contain the release, eol, or created dates like |
735 | + get_release_from_distro_info does.""" |
736 | + bsc = BootSourceCache.objects.filter( |
737 | + ( |
738 | + Q(subarch="hwe-%s" % string) & |
739 | + Q(release_title__startswith=string) | Q(release__startswith=string) |
740 | + ) | |
741 | + Q(release__startswith=string) | |
742 | + Q(release_title__startswith=string)).first() |
743 | + if bsc is None: |
744 | + return None |
745 | + elif None in (bsc.release_title, bsc.release, bsc.release_codename): |
746 | + return None |
747 | + else: |
748 | + return { |
749 | + 'version': bsc.release_title, |
750 | + 'eol-server': bsc.support_eol, |
751 | + 'series': bsc.release, |
752 | + 'codename': bsc.release_codename, |
753 | + } |
754 | + |
755 | + |
756 | +def get_release_version_from_string(string): |
757 | + """Convert an Ubuntu release, version, or kernel into a version tuple. |
758 | + |
759 | + Takes a string input represneting an Ubuntu release, version, or hwe_kernel |
760 | + and returns a version tuple. The return value is a three integer tuple |
761 | + representing an Ubuntu major, minor values(e.g 16, 4 for Xenial) and a |
762 | + weight. The weight is used to give edge kernels a higher value when |
763 | + compared to a non-edge kernel. Rolling kernels and releases are given a |
764 | + very high value (999, 999) to always be the higher value during comparison. |
765 | + |
766 | + Input: xenial, 16.04, hwe-16.04, hwe-16.04-lowlatency |
767 | + Output: (16, 4, 0) |
768 | + |
769 | + Input: hwe-16.04-edge |
770 | + Output: (16, 4, 1) |
771 | + |
772 | + Input: rolling, hwe-rolling, hwe-rolling-lowlatency |
773 | + Output: (999, 999, 0) |
774 | + |
775 | + Input: hwe-rolling-edge, hwe-rolling-lowlatency-edge |
776 | + Output: (999, 999, 1) |
777 | + """ |
778 | + parts = string.split('-') |
779 | + parts_len = len(parts) |
780 | + if parts_len == 1: |
781 | + # Just the release name, e.g xenial or 16.04 |
782 | + release = string |
783 | + edge = False |
784 | + elif parts_len == 2: |
785 | + # hwe kernel, e.g hwe-x or hwe-16.04 |
786 | + release = parts[1] |
787 | + edge = False |
788 | + elif parts_len == 3: |
789 | + # hwe edge or lowlatency kernel, |
790 | + # e.g hwe-16.04-edge or hwe-16.04-lowlatency |
791 | + release = parts[1] |
792 | + if parts[2] == 'edge': |
793 | + edge = True |
794 | + else: |
795 | + edge = False |
796 | + elif parts_len == 4: |
797 | + # hwe edge lowlatency kernel, e.g hwe-16.04-lowlatency-edge |
798 | + release = parts[1] |
799 | + if parts[3] == 'edge': |
800 | + edge = True |
801 | + else: |
802 | + edge = False |
803 | + else: |
804 | + raise ValueError("Unknown release or kernel %s!" % string) |
805 | + |
806 | + if release == 'rolling': |
807 | + # Rolling kernels are always the latest |
808 | + version = [999, 999] |
809 | + else: |
810 | + # First try to get release info from the SimpleStream |
811 | + ubuntu_release = get_release_from_db(release) |
812 | + if ubuntu_release is None: |
813 | + # Fall back on using the UbuntuDistroInfo library |
814 | + ubuntu_release = get_release_from_distro_info(release) |
815 | + if ubuntu_release is None: |
816 | + raise ValueError( |
817 | + "%s not found amoungst the known Ubuntu releases!" % string) |
818 | + # Remove 'LTS' from version if it exists |
819 | + version = ubuntu_release['version'].split(' ')[0] |
820 | + # Convert the version into a list of ints |
821 | + version = [int(seg) for seg in version.split('.')] |
822 | + if edge: |
823 | + # Ensure edge kernels are viewed as newer than non-edge |
824 | + version += [1] |
825 | + else: |
826 | + version += [0] |
827 | + return tuple(version) |
828 | + |
829 | + |
830 | def release_a_newer_than_b(a, b): |
831 | """Compare two Ubuntu releases and return true if a >= b. |
832 | |
833 | - The release names can be the full release name(e.g Precise, Trusty), or |
834 | - a hardware enablement(e.g hwe-p, hwe-t). The function wraps around the |
835 | - letter 'p' as Precise was the first version of Ubuntu MAAS supported |
836 | + The release names can be the full release name(e.g Precise, Trusty), |
837 | + release versions(e.g 12.04, 16.04), or an hwe kernel(e.g hwe-p, hwe-16.04, |
838 | + hwe-rolling-lowlatency-edge). |
839 | """ |
840 | - def get_release_num(release): |
841 | - release = release.lower() |
842 | - if 'hwe-' in release: |
843 | - release = release.replace('hwe-', '') |
844 | - return ord(release[0]) |
845 | - |
846 | - # Compare release versions based off of the first letter of their |
847 | - # release name or the letter in hwe-<letter>. Wrap around the letter |
848 | - # 'p' as that is the first version of Ubuntu MAAS supported. |
849 | - num_a = get_release_num(a) |
850 | - num_b = get_release_num(b) |
851 | - num_wrap = ord('p') |
852 | - |
853 | - if((num_a >= num_wrap and num_b >= num_wrap and num_a >= num_b) or |
854 | - (num_a < num_wrap and num_b >= num_wrap and num_a < num_b) or |
855 | - (num_a < num_wrap and num_b < num_wrap and num_a >= num_b)): |
856 | - return True |
857 | - else: |
858 | - return False |
859 | + ver_a = get_release_version_from_string(a) |
860 | + ver_b = get_release_version_from_string(b) |
861 | + return ver_a >= ver_b |
862 | |
863 | |
864 | def validate_hwe_kernel( |
865 | @@ -306,10 +417,10 @@ |
866 | subarch) |
867 | |
868 | os_release = osystem + '/' + distro_series |
869 | - usable_kernels = BootResource.objects.get_usable_hwe_kernels( |
870 | - os_release, arch) |
871 | |
872 | if hwe_kernel and hwe_kernel.startswith('hwe-'): |
873 | + usable_kernels = BootResource.objects.get_usable_hwe_kernels( |
874 | + os_release, arch) |
875 | if hwe_kernel not in usable_kernels: |
876 | raise ValidationError( |
877 | '%s is not available for %s on %s.' % |
878 | @@ -324,6 +435,13 @@ |
879 | (hwe_kernel, min_hwe_kernel)) |
880 | return hwe_kernel |
881 | elif(min_hwe_kernel and min_hwe_kernel.startswith('hwe-')): |
882 | + kernel_parts = min_hwe_kernel.split('-') |
883 | + if len(kernel_parts) == 2: |
884 | + kflavor = 'generic' |
885 | + else: |
886 | + kflavor = kernel_parts[2] |
887 | + usable_kernels = BootResource.objects.get_usable_hwe_kernels( |
888 | + os_release, arch, kflavor) |
889 | for i in usable_kernels: |
890 | if(release_a_newer_than_b(i, min_hwe_kernel) and |
891 | release_a_newer_than_b(i, distro_series)): |
892 | @@ -331,7 +449,11 @@ |
893 | raise ValidationError( |
894 | '%s has no kernels availible which meet min_hwe_kernel(%s).' % |
895 | (distro_series, min_hwe_kernel)) |
896 | - return 'hwe-' + distro_series[0] |
897 | + for kernel in BootResource.objects.get_usable_hwe_kernels( |
898 | + os_release, arch, 'generic'): |
899 | + if release_a_newer_than_b(kernel, distro_series): |
900 | + return kernel |
901 | + raise ValidationError('%s has no kernels available.' % distro_series) |
902 | |
903 | |
904 | def validate_min_hwe_kernel(min_hwe_kernel): |
905 | @@ -341,4 +463,5 @@ |
906 | usable_kernels = BootResource.objects.get_usable_hwe_kernels() |
907 | if min_hwe_kernel not in usable_kernels: |
908 | raise ValidationError('%s is not a usable kernel.' % min_hwe_kernel) |
909 | - return min_hwe_kernel |
910 | + else: |
911 | + return min_hwe_kernel |
912 | |
913 | === modified file 'src/maasserver/utils/tests/test_osystems.py' |
914 | --- src/maasserver/utils/tests/test_osystems.py 2016-06-29 09:52:24 +0000 |
915 | +++ src/maasserver/utils/tests/test_osystems.py 2016-08-31 06:33:18 +0000 |
916 | @@ -25,7 +25,10 @@ |
917 | from maasserver.utils import osystems as osystems_module |
918 | from maasserver.utils.osystems import ( |
919 | get_distro_series_initial, |
920 | + get_release_from_db, |
921 | + get_release_from_distro_info, |
922 | get_release_requires_key, |
923 | + get_release_version_from_string, |
924 | list_all_releases_requiring_keys, |
925 | list_all_usable_osystems, |
926 | list_all_usable_releases, |
927 | @@ -35,6 +38,7 @@ |
928 | make_hwe_kernel_ui_text, |
929 | release_a_newer_than_b, |
930 | validate_hwe_kernel, |
931 | + validate_min_hwe_kernel, |
932 | validate_osystem_and_distro_series, |
933 | ) |
934 | from maastesting.matchers import MockAnyCall |
935 | @@ -290,13 +294,10 @@ |
936 | self.assertEqual('trusty (hwe-t)', make_hwe_kernel_ui_text('hwe-t')) |
937 | |
938 | def test_make_hwe_kernel_ui_returns_kernel_when_none_found(self): |
939 | - # Since this is testing that our fall final fall back returns just the |
940 | - # kernel name when the release isn't found in BootSourceCache or |
941 | - # UbuntuDistroInfo we patch out UbuntuDistroInfo so nothing is found. |
942 | - self.patch(UbuntuDistroInfo, 'all').value = [] |
943 | + unknown_kernel = factory.make_name('kernel') |
944 | self.assertEqual( |
945 | - 'hwe-m', |
946 | - make_hwe_kernel_ui_text('hwe-m')) |
947 | + unknown_kernel, |
948 | + make_hwe_kernel_ui_text(unknown_kernel)) |
949 | |
950 | |
951 | class TestValidateOsystemAndDistroSeries(MAASServerTestCase): |
952 | @@ -339,20 +340,102 @@ |
953 | validate_osystem_and_distro_series(osystem['name'], release + '*')) |
954 | |
955 | |
956 | +class TestGetReleaseVersionFromString(MAASServerTestCase): |
957 | + |
958 | + def __init__(self, *args, **kwargs): |
959 | + super().__init__(*args, **kwargs) |
960 | + ubuntu = UbuntuDistroInfo() |
961 | + # We can't test with releases older than Precise as they have duplicate |
962 | + # names(e.g Wily and Warty) which will break the old style kernel |
963 | + # tests. |
964 | + valid_releases = [ |
965 | + row for row in ubuntu._rows |
966 | + if int(row['version'].split('.')[0]) >= 12 |
967 | + ] |
968 | + release = random.choice(valid_releases) |
969 | + # Remove 'LTS' from version if it exists |
970 | + version_str = release['version'].split(' ')[0] |
971 | + # Convert the version into a list of ints |
972 | + version_tuple = tuple([int(seg) for seg in version_str.split('.')]) |
973 | + |
974 | + self.scenarios = ( |
975 | + ("Release name", { |
976 | + "string": release['series'], |
977 | + "expected": version_tuple + tuple([0]), |
978 | + }), |
979 | + ("Release version", { |
980 | + "string": version_str, |
981 | + "expected": version_tuple + tuple([0]), |
982 | + }), |
983 | + ("Old style kernel", { |
984 | + "string": "hwe-%s" % release['series'][0], |
985 | + "expected": version_tuple + tuple([0]), |
986 | + }), |
987 | + ("New style kernel", { |
988 | + "string": "hwe-%s" % version_str, |
989 | + "expected": version_tuple + tuple([0]), |
990 | + }), |
991 | + ("New style edge kernel", { |
992 | + "string": "hwe-%s-edge" % version_str, |
993 | + "expected": version_tuple + tuple([1]), |
994 | + }), |
995 | + ("New style low latency kernel", { |
996 | + "string": "hwe-%s-lowlatency" % version_str, |
997 | + "expected": version_tuple + tuple([0]), |
998 | + }), |
999 | + ("New style edge low latency kernel", { |
1000 | + "string": "hwe-%s-lowlatency-edge" % version_str, |
1001 | + "expected": version_tuple + tuple([1]), |
1002 | + }), |
1003 | + ("Rolling kernel", { |
1004 | + "string": "hwe-rolling", |
1005 | + "expected": tuple([999, 999, 0]), |
1006 | + }), |
1007 | + ("Rolling edge kernel", { |
1008 | + "string": "hwe-rolling-edge", |
1009 | + "expected": tuple([999, 999, 1]), |
1010 | + }), |
1011 | + ("Rolling lowlatency kernel", { |
1012 | + "string": "hwe-rolling-lowlatency", |
1013 | + "expected": tuple([999, 999, 0]), |
1014 | + }), |
1015 | + ("Rolling lowlatency edge kernel", { |
1016 | + "string": "hwe-rolling-lowlatency-edge", |
1017 | + "expected": tuple([999, 999, 1]), |
1018 | + }), |
1019 | + ) |
1020 | + |
1021 | + def test_get_release_version_from_string(self): |
1022 | + self.assertEquals( |
1023 | + self.expected, |
1024 | + get_release_version_from_string(self.string)) |
1025 | + |
1026 | + |
1027 | class TestReleaseANewerThanB(MAASServerTestCase): |
1028 | |
1029 | - def test_release_a_newer_than_b(self): |
1030 | - # Since we wrap around 'p' we want to use 'p' as our starting point |
1031 | - alphabet = ([chr(i) for i in range(ord('p'), ord('z') + 1)] + |
1032 | - [chr(i) for i in range(ord('a'), ord('p'))]) |
1033 | - previous_true = 0 |
1034 | - for i in alphabet: |
1035 | - true_count = 0 |
1036 | - for j in alphabet: |
1037 | - if release_a_newer_than_b('hwe-' + i, j): |
1038 | - true_count += 1 |
1039 | - previous_true += 1 |
1040 | - self.assertEqual(previous_true, true_count) |
1041 | + def test_a_newer_than_b_true(self): |
1042 | + self.assertTrue( |
1043 | + release_a_newer_than_b( |
1044 | + 'hwe-rolling', |
1045 | + factory.make_kernel_string(can_be_release_or_version=True))) |
1046 | + |
1047 | + def test_a_equal_to_b_true(self): |
1048 | + string = factory.make_kernel_string(can_be_release_or_version=True) |
1049 | + self.assertTrue(release_a_newer_than_b(string, string)) |
1050 | + |
1051 | + def test_a_less_than_b_false(self): |
1052 | + self.assertFalse( |
1053 | + release_a_newer_than_b( |
1054 | + factory.make_kernel_string(can_be_release_or_version=True), |
1055 | + 'hwe-rolling')) |
1056 | + |
1057 | + def test_accounts_for_edge(self): |
1058 | + self.assertFalse( |
1059 | + release_a_newer_than_b('hwe-rolling', 'hwe-rolling-edge')) |
1060 | + |
1061 | + def test_kernel_flavor_doesnt_make_difference(self): |
1062 | + self.assertTrue(release_a_newer_than_b( |
1063 | + 'hwe-rolling', 'hwe-rolling-lowlatency')) |
1064 | |
1065 | |
1066 | class TestValidateHweKernel(MAASServerTestCase): |
1067 | @@ -474,3 +557,108 @@ |
1068 | self.assertThat( |
1069 | mock_get_config, MockAnyCall('commissioning_distro_series')) |
1070 | self.assertEqual('hwe-v', kernel) |
1071 | + |
1072 | + |
1073 | +class TestValidateMinHweKernel(MAASServerTestCase): |
1074 | + |
1075 | + def test_validates_kernel(self): |
1076 | + kernel = factory.make_kernel_string(generic_only=True) |
1077 | + self.patch( |
1078 | + BootResource.objects, |
1079 | + 'get_usable_hwe_kernels').return_value = (kernel,) |
1080 | + self.assertEquals(kernel, validate_min_hwe_kernel(kernel)) |
1081 | + |
1082 | + def test_returns_empty_string_when_none(self): |
1083 | + self.assertEquals("", validate_min_hwe_kernel(None)) |
1084 | + |
1085 | + def test_raises_exception_when_not_found(self): |
1086 | + self.assertRaises( |
1087 | + ValidationError, |
1088 | + validate_min_hwe_kernel, factory.make_kernel_string()) |
1089 | + |
1090 | + def test_raises_exception_when_lowlatency(self): |
1091 | + self.assertRaises( |
1092 | + ValidationError, validate_min_hwe_kernel, 'hwe-16.04-lowlatency') |
1093 | + |
1094 | + |
1095 | +class TestGetReleaseFromDistroInfo(MAASServerTestCase): |
1096 | + |
1097 | + def pick_release(self): |
1098 | + ubuntu = UbuntuDistroInfo() |
1099 | + supported_releases = [ |
1100 | + release for release in ubuntu._rows |
1101 | + if int(release['version'].split('.')[0]) >= 12 |
1102 | + ] |
1103 | + return random.choice(supported_releases) |
1104 | + |
1105 | + def test_finds_by_series(self): |
1106 | + release = self.pick_release() |
1107 | + self.assertItemsEqual( |
1108 | + release, get_release_from_distro_info(release['series'])) |
1109 | + |
1110 | + def test_finds_by_series_first_letter(self): |
1111 | + release = self.pick_release() |
1112 | + self.assertItemsEqual( |
1113 | + release, get_release_from_distro_info(release['series'][0])) |
1114 | + |
1115 | + def test_finds_by_version(self): |
1116 | + release = self.pick_release() |
1117 | + self.assertItemsEqual( |
1118 | + release, get_release_from_distro_info(release['version'])) |
1119 | + |
1120 | + def test_returns_none_when_not_found(self): |
1121 | + self.assertIsNone( |
1122 | + get_release_from_distro_info(factory.make_name('string'))) |
1123 | + |
1124 | + |
1125 | +class TestGetReleaseFromDB(MAASServerTestCase): |
1126 | + |
1127 | + def make_boot_source_cache(self): |
1128 | + # Disable boot sources signals otherwise the test fails due to unrun |
1129 | + # post-commit tasks at the end of the test. |
1130 | + self.useFixture(SignalsDisabled("bootsources")) |
1131 | + ubuntu = UbuntuDistroInfo() |
1132 | + supported_releases = [ |
1133 | + release for release in ubuntu._rows |
1134 | + if int(release['version'].split('.')[0]) >= 12 |
1135 | + ] |
1136 | + release = random.choice(supported_releases) |
1137 | + subarch = "hwe-%s" % release['version'].split(' ')[0] |
1138 | + factory.make_BootSourceCache( |
1139 | + os='ubuntu', |
1140 | + arch=factory.make_name('arch'), |
1141 | + subarch=subarch, |
1142 | + release=release['series'], |
1143 | + release_codename=release['codename'], |
1144 | + release_title=release['version'], |
1145 | + support_eol=release['eol-server'], |
1146 | + ) |
1147 | + return release |
1148 | + |
1149 | + def test_finds_by_subarch(self): |
1150 | + release = self.make_boot_source_cache() |
1151 | + self.assertEquals( |
1152 | + release['series'], |
1153 | + get_release_from_db(release['version'].split(' ')[0])['series']) |
1154 | + |
1155 | + def test_finds_by_release(self): |
1156 | + release = self.make_boot_source_cache() |
1157 | + self.assertEquals( |
1158 | + release['version'], |
1159 | + get_release_from_db(release['series'])['version']) |
1160 | + |
1161 | + def test_finds_by_release_first_letter(self): |
1162 | + release = self.make_boot_source_cache() |
1163 | + self.assertEquals( |
1164 | + release['version'], |
1165 | + get_release_from_db(release['series'][0])['version']) |
1166 | + |
1167 | + def test_finds_by_version(self): |
1168 | + release = self.make_boot_source_cache() |
1169 | + self.assertItemsEqual( |
1170 | + release['series'], |
1171 | + get_release_from_db(release['version'])['series']) |
1172 | + |
1173 | + def test_returns_none_when_not_found(self): |
1174 | + self.assertIsNone( |
1175 | + get_release_from_db(factory.make_name('string'))) |
1176 | |
1177 | === modified file 'src/maasserver/websockets/handlers/general.py' |
1178 | --- src/maasserver/websockets/handlers/general.py 2016-08-18 15:42:50 +0000 |
1179 | +++ src/maasserver/websockets/handlers/general.py 2016-08-31 06:33:18 +0000 |
1180 | @@ -46,6 +46,7 @@ |
1181 | 'known_architectures', |
1182 | 'pockets_to_disable', |
1183 | 'hwe_kernels', |
1184 | + 'min_hwe_kernels', |
1185 | 'default_min_hwe_kernel', |
1186 | 'osinfo', |
1187 | 'machine_actions', |
1188 | @@ -77,6 +78,16 @@ |
1189 | return list_hwe_kernel_choices( |
1190 | BootResource.objects.get_usable_hwe_kernels()) |
1191 | |
1192 | + def min_hwe_kernels(self, params): |
1193 | + """Return all supported min_hwe_kernels. |
1194 | + |
1195 | + This filters out all non-generic kernel flavors. The user can select |
1196 | + the flavor during deployment. |
1197 | + """ |
1198 | + return list_hwe_kernel_choices( |
1199 | + BootResource.objects.get_usable_hwe_kernels() |
1200 | + ) |
1201 | + |
1202 | def default_min_hwe_kernel(self, params): |
1203 | """Return the default_min_hwe_kernel.""" |
1204 | return Config.objects.get_config('default_min_hwe_kernel') |
1205 | |
1206 | === modified file 'src/maasserver/websockets/handlers/tests/test_general.py' |
1207 | --- src/maasserver/websockets/handlers/tests/test_general.py 2016-08-18 15:42:50 +0000 |
1208 | +++ src/maasserver/websockets/handlers/tests/test_general.py 2016-08-31 06:33:18 +0000 |
1209 | @@ -29,6 +29,12 @@ |
1210 | |
1211 | class TestGeneralHandler(MAASServerTestCase): |
1212 | |
1213 | + def setUp(self): |
1214 | + super().setUp() |
1215 | + # Disable boot sources signals otherwise the test fails due to unrun |
1216 | + # post-commit tasks at the end of the test. |
1217 | + self.useFixture(SignalsDisabled("bootsources")) |
1218 | + |
1219 | def dehydrate_actions(self, actions, node_type=None): |
1220 | return [ |
1221 | { |
1222 | @@ -40,6 +46,41 @@ |
1223 | if node_type is None or node_type in action.for_type |
1224 | ] |
1225 | |
1226 | + def make_boot_sources(self): |
1227 | + kernels = [] |
1228 | + ubuntu = UbuntuDistroInfo() |
1229 | + for row in ubuntu._rows: |
1230 | + release_year = int(row['version'].split('.')[0]) |
1231 | + if release_year < 12: |
1232 | + continue |
1233 | + elif release_year < 16: |
1234 | + style = row['series'][0] |
1235 | + else: |
1236 | + style = row['version'] |
1237 | + for kflavor in [ |
1238 | + 'generic', 'lowlatency', 'edge', 'lowlatency-edge']: |
1239 | + if kflavor == 'generic': |
1240 | + kernel = "hwe-%s" % style |
1241 | + else: |
1242 | + kernel = "hwe-%s-%s" % (style, kflavor) |
1243 | + arch = factory.make_name('arch') |
1244 | + architecture = "%s/%s" % (arch, kernel) |
1245 | + release = row['series'].split(' ')[0] |
1246 | + factory.make_usable_boot_resource( |
1247 | + name="ubuntu/" + release, |
1248 | + kflavor=kflavor, |
1249 | + extra={'subarches': kernel}, |
1250 | + architecture=architecture, |
1251 | + rtype=BOOT_RESOURCE_TYPE.SYNCED) |
1252 | + factory.make_BootSourceCache( |
1253 | + os="ubuntu", |
1254 | + arch=arch, |
1255 | + subarch=kernel, |
1256 | + release=release) |
1257 | + kernels.append( |
1258 | + (kernel, '%s (%s)' % (release, kernel))) |
1259 | + return kernels |
1260 | + |
1261 | def test_architectures(self): |
1262 | arches = [ |
1263 | "%s/%s" % (factory.make_name("arch"), factory.make_name("subarch")) |
1264 | @@ -63,41 +104,19 @@ |
1265 | handler.pockets_to_disable({})) |
1266 | |
1267 | def test_hwe_kernels(self): |
1268 | - ubuntu_releases = UbuntuDistroInfo() |
1269 | - expected_output = [] |
1270 | - # Disable boot sources signals otherwise the test fails due to unrun |
1271 | - # post-commit tasks at the end of the test. |
1272 | - self.useFixture(SignalsDisabled("bootsources")) |
1273 | - # Start with the first release MAAS supported. We do this |
1274 | - # because the lookup between hwe- kernel and release can fail |
1275 | - # when multiple releases start with the same letter. For |
1276 | - # example both warty(4.10) and wily(15.10) will have an hwe-w |
1277 | - # kernel. Because of this the mapping between kernel and |
1278 | - # release will pick the release which was downloaded |
1279 | - # first. Since precise no release has used the same first |
1280 | - # letter so we do not have this problem with supported |
1281 | - # releases. |
1282 | - for release in ubuntu_releases.all[ |
1283 | - ubuntu_releases.all.index('precise'):]: |
1284 | - kernel = 'hwe-' + release[0] |
1285 | - arch = factory.make_name('arch') |
1286 | - architecture = "%s/%s" % (arch, kernel) |
1287 | - factory.make_usable_boot_resource( |
1288 | - name="ubuntu/" + release, |
1289 | - extra={'subarches': kernel}, |
1290 | - architecture=architecture, |
1291 | - rtype=BOOT_RESOURCE_TYPE.SYNCED) |
1292 | - factory.make_BootSourceCache( |
1293 | - os="ubuntu", |
1294 | - arch=arch, |
1295 | - subarch=kernel, |
1296 | - release=release) |
1297 | - expected_output.append((kernel, '%s (%s)' % (release, kernel))) |
1298 | + expected_output = self.make_boot_sources() |
1299 | handler = GeneralHandler(factory.make_User(), {}) |
1300 | self.assertItemsEqual( |
1301 | sorted(expected_output, key=lambda choice: choice[0]), |
1302 | sorted(handler.hwe_kernels({}), key=lambda choice: choice[0])) |
1303 | |
1304 | + def test_hwe_min_kernels(self): |
1305 | + expected_output = self.make_boot_sources() |
1306 | + handler = GeneralHandler(factory.make_User(), {}) |
1307 | + self.assertItemsEqual( |
1308 | + sorted(expected_output, key=lambda choice: choice[0]), |
1309 | + sorted(handler.min_hwe_kernels({}), key=lambda choice: choice[0])) |
1310 | + |
1311 | def test_osinfo(self): |
1312 | handler = GeneralHandler(factory.make_User(), {}) |
1313 | osystem = make_osystem_with_releases(self) |
1314 | |
1315 | === modified file 'src/maastesting/factory.py' |
1316 | --- src/maastesting/factory.py 2016-08-18 11:02:26 +0000 |
1317 | +++ src/maastesting/factory.py 2016-08-31 06:33:18 +0000 |
1318 | @@ -32,6 +32,7 @@ |
1319 | import urllib.request |
1320 | from uuid import uuid1 |
1321 | |
1322 | +from distro_info import UbuntuDistroInfo |
1323 | from maastesting.fixtures import TempDirectory |
1324 | from netaddr import ( |
1325 | IPAddress, |
1326 | @@ -619,5 +620,30 @@ |
1327 | cmd=[self.make_name("command")], |
1328 | output=factory.make_bytes()) |
1329 | |
1330 | + def make_kernel_string( |
1331 | + self, can_be_release_or_version=False, generic_only=False): |
1332 | + ubuntu = UbuntuDistroInfo() |
1333 | + # Only select from MAAS supported releases so we don't have to deal |
1334 | + # with versions name overlap(e.g Warty and Wily). |
1335 | + supported_releases = [ |
1336 | + release for release in ubuntu._rows |
1337 | + if int(release['version'].split('.')[0]) >= 12 |
1338 | + ] |
1339 | + release = random.choice(supported_releases) |
1340 | + # Remove 'LTS' from version if it exists |
1341 | + version_str = release['version'].split(' ')[0] |
1342 | + strings = [ |
1343 | + "hwe-%s" % release['series'][0], "hwe-%s" % version_str, |
1344 | + "hwe-%s-edge" % version_str, |
1345 | + ] |
1346 | + if not generic_only: |
1347 | + strings += [ |
1348 | + "hwe-%s-lowlatency" % version_str, |
1349 | + "hwe-%s-lowlatency-edge" % version_str, |
1350 | + ] |
1351 | + if can_be_release_or_version: |
1352 | + strings += [release['series'], version_str] |
1353 | + return random.choice(strings) |
1354 | + |
1355 | # Create factory singleton. |
1356 | factory = Factory() |
1357 | |
1358 | === modified file 'src/provisioningserver/import_images/download_descriptions.py' |
1359 | --- src/provisioningserver/import_images/download_descriptions.py 2016-06-16 20:51:55 +0000 |
1360 | +++ src/provisioningserver/import_images/download_descriptions.py 2016-08-31 06:33:18 +0000 |
1361 | @@ -36,7 +36,7 @@ |
1362 | """Return a subset of dict `item` for storing in a boot images dict.""" |
1363 | keys_to_keep = [ |
1364 | 'content_id', 'product_name', 'version_name', 'path', 'subarches', |
1365 | - 'release_codename', 'release_title', 'support_eol'] |
1366 | + 'release_codename', 'release_title', 'support_eol', 'kflavor'] |
1367 | compact_item = { |
1368 | key: item[key] |
1369 | for key in keys_to_keep |
1370 | @@ -94,12 +94,14 @@ |
1371 | self.boot_images_dict.set( |
1372 | base_image._replace(subarch=subarch), compact_item) |
1373 | |
1374 | - # HWE resources with generic, should map to the HWE that ships with |
1375 | - # that release. |
1376 | - hwe_arch = 'hwe-%s' % release[0] |
1377 | - if subarch == hwe_arch and 'generic' in subarches: |
1378 | - self.boot_images_dict.set( |
1379 | - base_image._replace(subarch='generic'), compact_item) |
1380 | + if os == 'ubuntu' and item.get('version') is not None: |
1381 | + # HWE resources with generic, should map to the HWE that ships with |
1382 | + # that release. Starting with Xenial kernels changed from using the |
1383 | + # naming format hwe-<letter> to hwe-<release>. Look for both. |
1384 | + hwe_archs = ["hwe-%s" % item['version'], "hwe-%s" % release[0]] |
1385 | + if subarch in hwe_archs and 'generic' in subarches: |
1386 | + self.boot_images_dict.set( |
1387 | + base_image._replace(subarch='generic'), compact_item) |
1388 | |
1389 | def sync(self, reader, path): |
1390 | try: |
1391 | |
1392 | === modified file 'src/provisioningserver/import_images/tests/test_boot_resources.py' |
1393 | --- src/provisioningserver/import_images/tests/test_boot_resources.py 2016-07-06 18:30:19 +0000 |
1394 | +++ src/provisioningserver/import_images/tests/test_boot_resources.py 2016-08-31 06:33:18 +0000 |
1395 | @@ -476,6 +476,7 @@ |
1396 | self.assertItemsEqual( |
1397 | [ |
1398 | 'content_id', |
1399 | + 'kflavor', |
1400 | 'path', |
1401 | 'product_name', |
1402 | 'version_name', |
1403 | |
1404 | === modified file 'src/provisioningserver/import_images/tests/test_download_descriptions.py' |
1405 | --- src/provisioningserver/import_images/tests/test_download_descriptions.py 2016-07-06 18:43:07 +0000 |
1406 | +++ src/provisioningserver/import_images/tests/test_download_descriptions.py 2016-08-31 06:33:18 +0000 |
1407 | @@ -234,12 +234,14 @@ |
1408 | class TestRepoDumper(MAASTestCase): |
1409 | """Tests for `RepoDumper`.""" |
1410 | |
1411 | - def make_item(self, os=None, release=None, arch=None, |
1412 | + def make_item(self, os=None, release=None, version=None, arch=None, |
1413 | subarch=None, subarches=None, label=None): |
1414 | if os is None: |
1415 | os = factory.make_name('os') |
1416 | if release is None: |
1417 | release = factory.make_name('release') |
1418 | + if version is None: |
1419 | + version = factory.make_name('version') |
1420 | if arch is None: |
1421 | arch = factory.make_name('arch') |
1422 | if subarch is None: |
1423 | @@ -257,6 +259,7 @@ |
1424 | 'path': factory.make_name('path'), |
1425 | 'os': os, |
1426 | 'release': release, |
1427 | + 'version': version, |
1428 | 'arch': arch, |
1429 | 'subarch': subarch, |
1430 | 'subarches': ','.join(subarches), |
1431 | @@ -307,7 +310,7 @@ |
1432 | label=item['label']) |
1433 | self.assertEqual(compat_item, boot_images_dict.mapping[image_spec]) |
1434 | |
1435 | - def test_insert_item_sets_generic_to_release_item_for_hwe(self): |
1436 | + def test_insert_item_sets_generic_to_release_item_for_hwe_letter(self): |
1437 | boot_images_dict = BootImageMapping() |
1438 | dumper = RepoDumper(boot_images_dict) |
1439 | os = 'ubuntu' |
1440 | @@ -338,6 +341,37 @@ |
1441 | label=label) |
1442 | self.assertEqual(compat_item, boot_images_dict.mapping[image_spec]) |
1443 | |
1444 | + def test_insert_item_sets_generic_to_release_item_for_hwe_version(self): |
1445 | + boot_images_dict = BootImageMapping() |
1446 | + dumper = RepoDumper(boot_images_dict) |
1447 | + os = 'ubuntu' |
1448 | + release = 'xenial' |
1449 | + arch = 'amd64' |
1450 | + label = 'release' |
1451 | + hwep_subarch = 'hwe-16.04' |
1452 | + hwep_subarches = ['generic', 'hwe-16.04', 'hwe-16.10'] |
1453 | + hwes_subarch = 'hwe-16.10' |
1454 | + hwes_subarches = ['generic', 'hwe-16.04', 'hwe-16.10'] |
1455 | + hwep_item, compat_item = self.make_item( |
1456 | + os=os, release=release, |
1457 | + arch=arch, subarch=hwep_subarch, |
1458 | + subarches=hwep_subarches, label=label) |
1459 | + hwes_item, _ = self.make_item( |
1460 | + os=os, release=release, |
1461 | + arch=arch, subarch=hwes_subarch, |
1462 | + subarches=hwes_subarches, label=label) |
1463 | + self.patch( |
1464 | + download_descriptions, |
1465 | + 'products_exdata').side_effect = [hwep_item, hwes_item] |
1466 | + for _ in range(2): |
1467 | + dumper.insert_item( |
1468 | + sentinel.data, sentinel.src, sentinel.target, |
1469 | + sentinel.pedigree, sentinel.contentsource) |
1470 | + image_spec = make_image_spec( |
1471 | + os=os, release=release, arch=arch, subarch='generic', |
1472 | + label=label) |
1473 | + self.assertEqual(compat_item, boot_images_dict.mapping[image_spec]) |
1474 | + |
1475 | def test_sync_does_not_propagate_ioerror(self): |
1476 | mock_sync = self.patch(download_descriptions.BasicMirrorWriter, "sync") |
1477 | mock_sync.side_effect = IOError() |
1478 | |
1479 | === modified file 'utilities/check-imports' |
1480 | --- utilities/check-imports 2016-08-15 16:41:18 +0000 |
1481 | +++ utilities/check-imports 2016-08-31 06:33:18 +0000 |
1482 | @@ -147,6 +147,7 @@ |
1483 | StandardLibraries = Pattern( |
1484 | map("{0}|{0}.**".format, python_standard_libs)) |
1485 | TestingLibraries = Pattern( |
1486 | + "distro_info.UbuntuDistroInfo", |
1487 | "django_nose|django_nose.**", |
1488 | "dns|dns.**", |
1489 | "fixtures|fixtures.**", |
Lots of comments and identified with some key issues with this branch. Some decisions need to be made to make sure this is the correct path.