Merge lp:~ltrager/maas/hwe_backend into lp:~maas-committers/maas/trunk
- hwe_backend
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Lee Trager |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4160 |
Proposed branch: | lp:~ltrager/maas/hwe_backend |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
580 lines (+436/-2) 8 files modified
src/maasserver/api/pxeconfig.py (+11/-0) src/maasserver/api/tests/test_node.py (+165/-0) src/maasserver/forms.py (+91/-0) src/maasserver/models/bootresource.py (+40/-0) src/maasserver/models/tests/test_bootresource.py (+67/-0) src/maasserver/preseed.py (+21/-1) src/maasserver/tests/test_forms_node.py (+14/-0) src/maasserver/tests/test_preseed.py (+27/-1) |
To merge this branch: | bzr merge lp:~ltrager/maas/hwe_backend |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Lee Trager (community) | Needs Resubmitting | ||
Mike Pontillo (community) | Abstain | ||
Jason Hobbs | Pending | ||
Review via email: mp+266163@code.launchpad.net |
Commit message
Enable deploying a specific kernel to a node with validation
If hwe_kernel is specified validate that its newer than min_hwe_kernel and newer than the selected Ubuntu release. If just min_hwe_kernel is specified and the release is older MAAS will automatically select a newer kernel.
Description of the change
Scott Moser (smoser) wrote : | # |
Christian Reis (kiko) wrote : | # |
On Wed, Jul 29, 2015 at 12:17:30AM -0000, Lee Trager wrote:
> + print(response.
^^^
> + def get_usable_
> + """Return the set of usable kernels for architecture and release."""
> + kernels = set()
> + for resource in self.filter(
> + architecture_
> + resource_set = resource.
> + if(resource_set is not None and
> + resource_
> + resource_
Would some continues make this ladder less tall?
--
Christian Robottom Reis | [+1] 612 888 4935 | http://
Canonical VP Hyperscale | [+55 16] 9 9112 6430
Lee Trager (ltrager) wrote : | # |
ah sorry wasn't ready to set this for review. I'm still finishing this up. This code will fix 1473167 by checking that the specified kernel is available during deployment. This will also check that the specified kernel is newer than the min_hwe_kernel.
Blake helped me with get_usable_kernels I'll take another look to see if I can break it down some more.
Blake Rouse (blake-rouse) wrote : | # |
Got lots of comments. This is missing lots of tests and I have some questions and some suggestions that need to be addressed.
Mike Pontillo (mpontillo) wrote : | # |
Looks like Blake has got this one well-covered. I'll leave it to him.
Lee Trager (ltrager) : | # |
Lee Trager (ltrager) wrote : | # |
I've made the corrections Blake has asked and added a bunch of tests.
Christian, I looked over get_usable_
arches = set()
for resource in self.all():
resource_set = resource.
if (not resource_set or
not resource_
not resource_
continue
arches.
if 'subarches' in resource.extra:
arch, _ = resource.
for subarch in resource.
return arches
Blake Rouse (blake-rouse) wrote : | # |
This is looking very very close. Only a couple fixes that are still required.
As for the testing of the Form, you are doing it all at the API level. This is okay as I don't want you to have to go and change it all. Normally what we do is test in at the Form level first. Its easier to setup for testing and any exceptions will be raise in the test. By testing at the API level the exceptions will not be raised you have to check the HTTP code to make sure no errors happened. You also need to test at the API level, but just based stuff like: did it use the form, did it raise a validation error, did it return a result. All the other complex combinations of validation should be tested at the form level.
Also the "Resubmit" is to be used by a review to say, please redo the entire branch and resubmit as something is really wrong. Like its targeted to the wrong branch or something. If you need another review from the reviewer just comment on the MP or message them on IRC.
Blake Rouse (blake-rouse) wrote : | # |
This looks really good. Not going to block you on the one comment, but I do believe it needs fixing before this branch lands.
Also have you been doing deployments with the branch and the HWE kernel to make sure this is getting you the expected resulted on a deployed node?
Lee Trager (ltrager) wrote : | # |
I've expanded the get_usable_kernels test, let me know if you think it needs more.
I've been running a full make test as well as deploying the proposed bits into a virtual environment to ensure everything works as it should in the real world. Right now most of my testing has been while using the CLI instead of UI. My next patchset will be focused on the UI
Preview Diff
1 | === modified file 'src/maasserver/api/pxeconfig.py' |
2 | --- src/maasserver/api/pxeconfig.py 2015-07-30 18:14:27 +0000 |
3 | +++ src/maasserver/api/pxeconfig.py 2015-08-05 20:09:04 +0000 |
4 | @@ -203,6 +203,17 @@ |
5 | hostname = strip_domain(node.hostname) |
6 | nodegroup = node.nodegroup |
7 | domain = nodegroup.name |
8 | + |
9 | + # Pre MAAS-1.9 the subarchitecture defined any kernel the node needed |
10 | + # to be able to boot. This could be a hardware enablement kernel(e.g |
11 | + # hwe-t) or something like highbank. With MAAS-1.9 any hardware |
12 | + # enablement kernel must be specifed in the hwe_kernel field, any other |
13 | + # kernel, such as highbank, is still specifed as a |
14 | + # subarchitecture. Since Ubuntu does not support architecture specific |
15 | + # hardware enablement kernels(i.e a highbank hwe-t kernel on precise) |
16 | + # we give precedence to any kernel defined in the subarchitecture field |
17 | + if subarch == "generic" and node.hwe_kernel: |
18 | + subarch = node.hwe_kernel |
19 | else: |
20 | nodegroup = find_nodegroup_for_pxeconfig_request(request) |
21 | preseed_url = compose_enlistment_preseed_url(nodegroup=nodegroup) |
22 | |
23 | === modified file 'src/maasserver/api/tests/test_node.py' |
24 | --- src/maasserver/api/tests/test_node.py 2015-07-27 20:23:17 +0000 |
25 | +++ src/maasserver/api/tests/test_node.py 2015-08-05 20:09:04 +0000 |
26 | @@ -37,6 +37,7 @@ |
27 | MAC_ERROR_MSG, |
28 | ) |
29 | from maasserver.models import ( |
30 | + BootResource, |
31 | MACAddress, |
32 | Node, |
33 | node as node_module, |
34 | @@ -524,7 +525,29 @@ |
35 | self.assertEqual(httplib.OK, response.status_code) |
36 | self.assertEqual(user_data, NodeUserData.objects.get_user_data(node)) |
37 | |
38 | + def test_POST_start_sets_default_hwe_kernel(self): |
39 | + self.patch( |
40 | + BootResource.objects, |
41 | + 'get_usable_kernels').return_value = ('hwe-t', 'hwe-u') |
42 | + osystem = make_usable_osystem(self, 'ubuntu', ['trusty']) |
43 | + distro_series = osystem['default_release'] |
44 | + node = factory.make_Node( |
45 | + owner=self.logged_in_user, mac=True, power_type='ether_wake', |
46 | + architecture=make_usable_architecture(self)) |
47 | + response = self.client.post( |
48 | + self.get_node_uri(node), { |
49 | + 'op': 'start', |
50 | + 'distro_series': distro_series, |
51 | + 'osystem': osystem['name'], |
52 | + }) |
53 | + self.assertEqual(httplib.OK, response.status_code) |
54 | + self.assertEqual( |
55 | + 'hwe-' + distro_series[0], reload_object(node).hwe_kernel) |
56 | + |
57 | def test_POST_start_sets_hwe_kernel(self): |
58 | + self.patch( |
59 | + BootResource.objects, |
60 | + 'get_usable_kernels').return_value = ('hwe-t', 'hwe-v') |
61 | node = factory.make_Node( |
62 | owner=self.logged_in_user, mac=True, power_type='ether_wake', |
63 | architecture=make_usable_architecture(self)) |
64 | @@ -536,6 +559,148 @@ |
65 | self.assertEqual(httplib.OK, response.status_code) |
66 | self.assertEqual('hwe-v', reload_object(node).hwe_kernel) |
67 | |
68 | + def test_POST_start_fails_with_nongeneric_arch_and_hwe_kernel(self): |
69 | + osystem = make_usable_osystem(self, 'ubuntu', ['trusty']) |
70 | + distro_series = osystem['default_release'] |
71 | + node = factory.make_Node( |
72 | + owner=self.logged_in_user, mac=True, power_type='ether_wake', |
73 | + architecture=make_usable_architecture(self)) |
74 | + response = self.client.post( |
75 | + self.get_node_uri(node), { |
76 | + 'op': 'start', |
77 | + 'hwe_kernel': 'hwe-v', |
78 | + 'distro_series': distro_series, |
79 | + 'osystem': osystem['name'], |
80 | + }) |
81 | + self.assertEqual(httplib.BAD_REQUEST, response.status_code, |
82 | + response.content) |
83 | + self.assertEqual( |
84 | + {'hwe_kernel': |
85 | + ["Subarchitecture(%s) must be generic when setting hwe_kernel." % |
86 | + node.split_arch()[1]]}, |
87 | + json.loads(response.content)) |
88 | + |
89 | + def test_POST_start_fails_with_missing_hwe_kernel(self): |
90 | + self.patch( |
91 | + BootResource.objects, |
92 | + 'get_usable_kernels').return_value = ('hwe-t', 'hwe-u') |
93 | + osystem = make_usable_osystem(self, 'ubuntu', ['trusty']) |
94 | + distro_series = osystem['default_release'] |
95 | + node = factory.make_Node( |
96 | + owner=self.logged_in_user, mac=True, power_type='ether_wake', |
97 | + architecture=make_usable_architecture( |
98 | + self, subarch_name='generic')) |
99 | + response = self.client.post( |
100 | + self.get_node_uri(node), { |
101 | + 'op': 'start', |
102 | + 'hwe_kernel': 'hwe-v', |
103 | + 'distro_series': distro_series, |
104 | + 'osystem': osystem['name'], |
105 | + }) |
106 | + self.assertEqual(httplib.BAD_REQUEST, response.status_code, |
107 | + response.content) |
108 | + self.assertEqual( |
109 | + {'hwe_kernel': |
110 | + ["hwe-v is not avaliable for %s/%s on %s." % |
111 | + (osystem['name'], distro_series, node.architecture)]}, |
112 | + json.loads(response.content)) |
113 | + |
114 | + def test_POST_start_fails_with_old_kernel_and_newer_release(self): |
115 | + self.patch( |
116 | + BootResource.objects, |
117 | + 'get_usable_kernels').return_value = ('hwe-t', 'hwe-v') |
118 | + osystem = make_usable_osystem(self, 'ubuntu', ['vivid']) |
119 | + distro_series = osystem['default_release'] |
120 | + node = factory.make_Node( |
121 | + owner=self.logged_in_user, mac=True, power_type='ether_wake', |
122 | + architecture=make_usable_architecture( |
123 | + self, subarch_name='generic')) |
124 | + response = self.client.post( |
125 | + self.get_node_uri(node), { |
126 | + 'op': 'start', |
127 | + 'distro_series': distro_series, |
128 | + 'osystem': osystem['name'], |
129 | + 'hwe_kernel': 'hwe-t', |
130 | + }) |
131 | + self.assertEqual(httplib.BAD_REQUEST, response.status_code, |
132 | + response.content) |
133 | + self.assertEqual( |
134 | + {'hwe_kernel': |
135 | + ["hwe-t is too old to use on ubuntu/vivid."]}, |
136 | + json.loads(response.content)) |
137 | + |
138 | + def test_POST_start_fails_with_old_kernel_and_newer_min_hwe_kernel(self): |
139 | + self.patch( |
140 | + BootResource.objects, |
141 | + 'get_usable_kernels').return_value = ('hwe-t', 'hwe-v') |
142 | + osystem = make_usable_osystem(self, 'ubuntu', ['precise']) |
143 | + distro_series = osystem['default_release'] |
144 | + node = factory.make_Node( |
145 | + owner=self.logged_in_user, mac=True, power_type='ether_wake', |
146 | + architecture=make_usable_architecture( |
147 | + self, subarch_name='generic'), |
148 | + min_hwe_kernel='hwe-v') |
149 | + response = self.client.post( |
150 | + self.get_node_uri(node), { |
151 | + 'op': 'start', |
152 | + 'distro_series': distro_series, |
153 | + 'osystem': osystem['name'], |
154 | + 'hwe_kernel': 'hwe-t', |
155 | + }) |
156 | + self.assertEqual(httplib.BAD_REQUEST, response.status_code, |
157 | + response.content) |
158 | + self.assertEqual( |
159 | + {'hwe_kernel': |
160 | + ["hwe_kernel(hwe-t) is older than min_hwe_kernel(hwe-v)."]}, |
161 | + json.loads(response.content)) |
162 | + |
163 | + def test_POST_start_fails_with_no_avalible_kernels(self): |
164 | + self.patch( |
165 | + BootResource.objects, |
166 | + 'get_usable_kernels').return_value = ('hwe-t', 'hwe-v') |
167 | + osystem = make_usable_osystem(self, 'ubuntu', ['precise']) |
168 | + distro_series = osystem['default_release'] |
169 | + node = factory.make_Node( |
170 | + owner=self.logged_in_user, mac=True, power_type='ether_wake', |
171 | + architecture=make_usable_architecture( |
172 | + self, subarch_name='generic'), |
173 | + min_hwe_kernel='hwe-v') |
174 | + response = self.client.post( |
175 | + self.get_node_uri(node), { |
176 | + 'op': 'start', |
177 | + 'distro_series': distro_series, |
178 | + 'osystem': osystem['name'], |
179 | + 'hwe_kernel': 'hwe-t', |
180 | + }) |
181 | + self.assertEqual(httplib.BAD_REQUEST, response.status_code, |
182 | + response.content) |
183 | + self.assertEqual( |
184 | + {'hwe_kernel': |
185 | + ["hwe_kernel(hwe-t) is older than min_hwe_kernel(hwe-v)."]}, |
186 | + json.loads(response.content)) |
187 | + |
188 | + def test_POST_start_with_old_release_and_newer_min_hwe_kernel(self): |
189 | + osystem = make_usable_osystem(self, 'ubuntu', ['trusty']) |
190 | + distro_series = osystem['default_release'] |
191 | + node = factory.make_Node( |
192 | + owner=self.logged_in_user, mac=True, power_type='ether_wake', |
193 | + architecture=make_usable_architecture( |
194 | + self, subarch_name='generic'), |
195 | + min_hwe_kernel='hwe-v') |
196 | + response = self.client.post( |
197 | + self.get_node_uri(node), { |
198 | + 'op': 'start', |
199 | + 'distro_series': distro_series, |
200 | + 'osystem': osystem['name'], |
201 | + }) |
202 | + self.assertEqual(httplib.BAD_REQUEST, response.status_code, |
203 | + response.content) |
204 | + self.assertEqual( |
205 | + {'hwe_kernel': |
206 | + ["trusty has no kernels availible which meet " + |
207 | + "min_hwe_kernel(hwe-v)."]}, |
208 | + json.loads(response.content)) |
209 | + |
210 | def test_POST_release_releases_owned_node(self): |
211 | self.patch(node_module, 'power_off_node') |
212 | self.patch(node_module.Node, 'start_transition_monitor') |
213 | |
214 | === modified file 'src/maasserver/forms.py' |
215 | --- src/maasserver/forms.py 2015-07-31 23:12:15 +0000 |
216 | +++ src/maasserver/forms.py 2015-08-05 20:09:04 +0000 |
217 | @@ -388,6 +388,96 @@ |
218 | |
219 | class NodeForm(MAASModelForm): |
220 | |
221 | + def _release_a_newer_than_b(self, a, b): |
222 | + """ Compare two Ubuntu releases and return true if a >= b |
223 | + |
224 | + The release names can be the full release name(e.g Precise, Trusty), or |
225 | + a hardware enablement(e.g hwe-p, hwe-t). The function wraps around the |
226 | + letter 'p' as Precise was the first version of Ubuntu MAAS supported |
227 | + """ |
228 | + def get_release_num(release): |
229 | + release = release.lower() |
230 | + if 'hwe-' in release: |
231 | + release = release.lstrip('hwe-') |
232 | + return ord(release[0]) |
233 | + |
234 | + # Compare release versions based off of the first letter of their |
235 | + # release name or the letter in hwe-<letter>. Wrap around the letter |
236 | + # 'p' as that is the first version of Ubuntu MAAS supported. |
237 | + num_a = get_release_num(a) |
238 | + num_b = get_release_num(b) |
239 | + num_wrap = ord('p') |
240 | + |
241 | + if((num_a >= num_wrap and num_b >= num_wrap and num_a >= num_b) or |
242 | + (num_a < num_wrap and num_b >= num_wrap and num_a < num_b) or |
243 | + (num_a < num_wrap and num_b < num_wrap and num_a >= num_b)): |
244 | + return True |
245 | + else: |
246 | + return False |
247 | + |
248 | + def _clean_hwe_kernel(self): |
249 | + hwe_kernel = self.cleaned_data.get('hwe_kernel') |
250 | + min_hwe_kernel = self.cleaned_data.get('min_hwe_kernel') |
251 | + architecture = self.cleaned_data.get('architecture') |
252 | + osystem = self.cleaned_data.get('osystem') |
253 | + distro_series = self.cleaned_data.get('distro_series') |
254 | + |
255 | + # The hwe_kernel feature is only supported on Ubuntu |
256 | + if((osystem and "ubuntu" not in osystem.lower()) or |
257 | + (not architecture or architecture == '') or |
258 | + (not distro_series or distro_series == '')): |
259 | + return hwe_kernel |
260 | + |
261 | + arch, subarch = architecture.split('/') |
262 | + |
263 | + if (subarch != 'generic' and |
264 | + (hwe_kernel.startswith('hwe-') or |
265 | + min_hwe_kernel.startswith('hwe-'))): |
266 | + set_form_error( |
267 | + self, 'hwe_kernel', |
268 | + 'Subarchitecture(%s) must be generic when setting hwe_kernel.' |
269 | + % subarch) |
270 | + return |
271 | + |
272 | + os_release = osystem + '/' + distro_series |
273 | + usable_kernels = BootResource.objects.get_usable_kernels( |
274 | + os_release, arch) |
275 | + |
276 | + if hwe_kernel.startswith('hwe-'): |
277 | + if hwe_kernel not in usable_kernels: |
278 | + set_form_error( |
279 | + self, 'hwe_kernel', |
280 | + '%s is not avaliable for %s on %s.' % |
281 | + (hwe_kernel, os_release, architecture)) |
282 | + return |
283 | + if not self._release_a_newer_than_b(hwe_kernel, distro_series): |
284 | + set_form_error( |
285 | + self, 'hwe_kernel', |
286 | + '%s is too old to use on %s.' % (hwe_kernel, os_release)) |
287 | + return |
288 | + |
289 | + if(hwe_kernel.startswith('hwe-') and |
290 | + min_hwe_kernel.startswith('hwe-') and |
291 | + not self._release_a_newer_than_b(hwe_kernel, min_hwe_kernel)): |
292 | + set_form_error( |
293 | + self, 'hwe_kernel', |
294 | + 'hwe_kernel(%s) is older than min_hwe_kernel(%s).' % |
295 | + (hwe_kernel, min_hwe_kernel)) |
296 | + return |
297 | + elif(min_hwe_kernel.startswith('hwe-')): |
298 | + for i in usable_kernels: |
299 | + if self._release_a_newer_than_b(i, min_hwe_kernel): |
300 | + return i |
301 | + set_form_error( |
302 | + self, 'hwe_kernel', |
303 | + '%s has no kernels availible which meet min_hwe_kernel(%s).' % |
304 | + (distro_series, min_hwe_kernel)) |
305 | + return |
306 | + elif hwe_kernel.strip() == '': |
307 | + return 'hwe-' + distro_series[0] |
308 | + |
309 | + return hwe_kernel |
310 | + |
311 | def __init__(self, request=None, *args, **kwargs): |
312 | super(NodeForm, self).__init__(*args, **kwargs) |
313 | # Even though it doesn't need it and doesn't use it, this form accepts |
314 | @@ -574,6 +664,7 @@ |
315 | # Take the default value from the node's cluster. |
316 | nodegroup = cleaned_data['nodegroup'] |
317 | cleaned_data['disable_ipv4'] = nodegroup.default_disable_ipv4 |
318 | + cleaned_data['hwe_kernel'] = self._clean_hwe_kernel() |
319 | return cleaned_data |
320 | |
321 | def is_valid(self): |
322 | |
323 | === modified file 'src/maasserver/models/bootresource.py' |
324 | --- src/maasserver/models/bootresource.py 2015-06-02 08:46:21 +0000 |
325 | +++ src/maasserver/models/bootresource.py 2015-08-05 20:09:04 +0000 |
326 | @@ -24,6 +24,7 @@ |
327 | ) |
328 | from maasserver import DefaultMeta |
329 | from maasserver.enum import ( |
330 | + BOOT_RESOURCE_FILE_TYPE, |
331 | BOOT_RESOURCE_TYPE, |
332 | BOOT_RESOURCE_TYPE_CHOICES, |
333 | BOOT_RESOURCE_TYPE_CHOICES_DICT, |
334 | @@ -223,6 +224,45 @@ |
335 | return False |
336 | return True |
337 | |
338 | + def get_usable_kernels(self, name, architecture): |
339 | + """Return the set of usable kernels for architecture and release.""" |
340 | + kernels = set() |
341 | + for resource in self.filter( |
342 | + architecture__startswith=architecture, name=name): |
343 | + resource_set = resource.get_latest_set() |
344 | + if(resource_set is None or |
345 | + not resource_set.commissionable or |
346 | + not resource_set.installable): |
347 | + continue |
348 | + subarch = resource.split_arch()[1] |
349 | + if subarch.startswith("hwe-"): |
350 | + kernels.add(subarch) |
351 | + if "subarches" in resource.extra: |
352 | + for subarch in resource.extra["subarches"].split(","): |
353 | + if subarch.startswith("hwe-"): |
354 | + kernels.add(subarch) |
355 | + return kernels |
356 | + |
357 | + def get_kpackage_for_node(self, node): |
358 | + """Return the kernel package name for the kernel specified.""" |
359 | + if not node.hwe_kernel: |
360 | + return None |
361 | + arch = node.split_arch()[0] |
362 | + os_release = node.get_osystem() + '/' + node.get_distro_series() |
363 | + # Before hwe_kernel was introduced the subarchitecture was the |
364 | + # hwe_kernel simple stream still uses this convention |
365 | + hwe_arch = arch + '/' + node.hwe_kernel |
366 | + |
367 | + resource = self.filter(name=os_release, architecture=hwe_arch).first() |
368 | + if resource: |
369 | + latest_set = resource.get_latest_set() |
370 | + if latest_set: |
371 | + kernel = latest_set.files.filter( |
372 | + filetype=BOOT_RESOURCE_FILE_TYPE.BOOT_KERNEL).first() |
373 | + if kernel and 'kpackage' in kernel.extra: |
374 | + return kernel.extra['kpackage'] |
375 | + return None |
376 | + |
377 | |
378 | def validate_architecture(value): |
379 | """Validates that architecture value contains a subarchitecture.""" |
380 | |
381 | === modified file 'src/maasserver/models/tests/test_bootresource.py' |
382 | --- src/maasserver/models/tests/test_bootresource.py 2015-05-07 18:14:38 +0000 |
383 | +++ src/maasserver/models/tests/test_bootresource.py 2015-08-05 20:09:04 +0000 |
384 | @@ -510,6 +510,73 @@ |
385 | self.assertTrue(BootResource.objects.boot_images_are_in_sync([image])) |
386 | |
387 | |
388 | +class TestGetUsableKernels(MAASServerTestCase): |
389 | + """Tests for `get_usable_kernels`.""" |
390 | + |
391 | + scenarios = ( |
392 | + ("ubuntu/trusty", { |
393 | + "name": "ubuntu/trusty", |
394 | + "arch": "amd64", |
395 | + "subarch": "generic", |
396 | + "kernels": ["hwe-t", "hwe-u", "hwe-v"], |
397 | + }), |
398 | + ("ubuntu/vivid", { |
399 | + "name": "ubuntu/vivid", |
400 | + "arch": "i386", |
401 | + "subarch": "generic", |
402 | + "kernels": ["hwe-v"], |
403 | + }), |
404 | + ("ubuntu/precise", { |
405 | + "name": "ubuntu/precise", |
406 | + "arch": "armfh", |
407 | + "subarch": "generic", |
408 | + "kernels": ["hwe-p", "hwe-t", "hwe-v"], |
409 | + }), |
410 | + ("ubuntu/wily", { |
411 | + "name": "ubuntu/wily", |
412 | + "arch": "armfh", |
413 | + "subarch": "hardbank", |
414 | + "kernels": [], |
415 | + })) |
416 | + |
417 | + def test__returns_usable_kernels(self): |
418 | + if self.subarch == "generic": |
419 | + for i in self.kernels: |
420 | + factory.make_usable_boot_resource( |
421 | + name=self.name, rtype=BOOT_RESOURCE_TYPE.SYNCED, |
422 | + architecture="%s/%s" % (self.arch, i)) |
423 | + else: |
424 | + factory.make_usable_boot_resource( |
425 | + name=self.name, rtype=BOOT_RESOURCE_TYPE.SYNCED, |
426 | + architecture="%s/%s" % (self.arch, self.subarch)) |
427 | + self.assertEqual( |
428 | + set(self.kernels), |
429 | + BootResource.objects.get_usable_kernels( |
430 | + self.name, self.arch), |
431 | + "%s should return %s as its usable kernel" % ( |
432 | + self.name, set(self.kernels))) |
433 | + |
434 | + |
435 | +class TestGetKpackageForNode(MAASServerTestCase): |
436 | + """Tests for `get_kpackage_for_node`.""" |
437 | + |
438 | + def test__returns_kpackage(self): |
439 | + resource = factory.make_BootResource( |
440 | + name="ubuntu/trusty", architecture="amd64/hwe-t", |
441 | + rtype=BOOT_RESOURCE_TYPE.SYNCED) |
442 | + resource_set = factory.make_BootResourceSet(resource) |
443 | + factory.make_boot_resource_file_with_content( |
444 | + resource_set, filename="boot-kernel", filetype="boot-kernel", |
445 | + extra={"kpackage": "linux-image-generic-lts-trusty"}) |
446 | + node = factory.make_Node( |
447 | + mac=True, power_type='ether_wake', osystem='ubuntu', |
448 | + distro_series='trusty', architecture='amd64/generic', |
449 | + hwe_kernel='hwe-t') |
450 | + self.assertEqual( |
451 | + "linux-image-generic-lts-trusty", |
452 | + BootResource.objects.get_kpackage_for_node(node)) |
453 | + |
454 | + |
455 | class TestBootResource(MAASServerTestCase): |
456 | """Tests for the `BootResource` model.""" |
457 | |
458 | |
459 | === modified file 'src/maasserver/preseed.py' |
460 | --- src/maasserver/preseed.py 2015-07-30 20:15:47 +0000 |
461 | +++ src/maasserver/preseed.py 2015-08-05 20:09:04 +0000 |
462 | @@ -50,6 +50,7 @@ |
463 | PreseedError, |
464 | ) |
465 | from maasserver.models import ( |
466 | + BootResource, |
467 | Config, |
468 | DHCPLease, |
469 | ) |
470 | @@ -225,6 +226,24 @@ |
471 | return [] |
472 | |
473 | |
474 | +def compose_curtin_kernel_preseed(node): |
475 | + """Return the curtin preseed for installing a kernel other than default. |
476 | + |
477 | + The BootResourceFile table contains a mapping between hwe kernels and |
478 | + Ubuntu package names. If this mapping is missing we fall back to letting |
479 | + Curtin figure out which kernel should be installed""" |
480 | + kpackage = BootResource.objects.get_kpackage_for_node(node) |
481 | + if kpackage: |
482 | + kernel_config = { |
483 | + 'kernel': { |
484 | + 'package': kpackage, |
485 | + 'mapping': {}, |
486 | + }, |
487 | + } |
488 | + return [yaml.safe_dump(kernel_config)] |
489 | + return [] |
490 | + |
491 | + |
492 | def get_curtin_userdata(node): |
493 | """Return the curtin user-data. |
494 | |
495 | @@ -237,6 +256,7 @@ |
496 | reporter_config = compose_curtin_maas_reporter(node) |
497 | network_config = compose_curtin_network_preseed_for(node) |
498 | swap_config = compose_curtin_swap_preseed(node) |
499 | + kernel_config = compose_curtin_kernel_preseed(node) |
500 | |
501 | # Get the storage configration if curtin supports custom storage. |
502 | storage_config = compose_curtin_storage_config(node) |
503 | @@ -251,7 +271,7 @@ |
504 | return pack_install( |
505 | configs=( |
506 | [main_config] + reporter_config + storage_config + |
507 | - network_config + swap_config), |
508 | + network_config + swap_config + kernel_config), |
509 | args=[installer_url]) |
510 | |
511 | |
512 | |
513 | === modified file 'src/maasserver/tests/test_forms_node.py' |
514 | --- src/maasserver/tests/test_forms_node.py 2015-07-25 04:02:23 +0000 |
515 | +++ src/maasserver/tests/test_forms_node.py 2015-08-05 20:09:04 +0000 |
516 | @@ -578,3 +578,17 @@ |
517 | AdminNodeForm(data={'nodegroup': new_nodegroup}, instance=node).save() |
518 | # The form saved without error, but the nodegroup change was ignored. |
519 | self.assertEqual(old_nodegroup, node.nodegroup) |
520 | + |
521 | + def test__release_a_newer_than_b(self): |
522 | + form = AdminNodeForm() |
523 | + # Since we wrap around 'p' we want to use 'p' as our starting point |
524 | + alphabet = ([chr(i) for i in xrange(ord('p'), ord('z') + 1)] + |
525 | + [chr(i) for i in xrange(ord('a'), ord('p'))]) |
526 | + previous_true = 0 |
527 | + for i in alphabet: |
528 | + true_count = 0 |
529 | + for j in alphabet: |
530 | + if form._release_a_newer_than_b(i, j): |
531 | + true_count += 1 |
532 | + previous_true += 1 |
533 | + self.assertEqual(previous_true, true_count) |
534 | |
535 | === modified file 'src/maasserver/tests/test_preseed.py' |
536 | --- src/maasserver/tests/test_preseed.py 2015-07-29 14:15:21 +0000 |
537 | +++ src/maasserver/tests/test_preseed.py 2015-08-05 20:09:04 +0000 |
538 | @@ -35,8 +35,12 @@ |
539 | MissingBootImage, |
540 | PreseedError, |
541 | ) |
542 | -from maasserver.models import Config |
543 | +from maasserver.models import ( |
544 | + BootResource, |
545 | + Config, |
546 | +) |
547 | from maasserver.preseed import ( |
548 | + compose_curtin_kernel_preseed, |
549 | compose_curtin_maas_reporter, |
550 | compose_curtin_network_preseed, |
551 | compose_curtin_swap_preseed, |
552 | @@ -837,6 +841,28 @@ |
553 | self.assertEqual(swap_preseed, ['swap: {size: 10000000000B}\n']) |
554 | |
555 | |
556 | +class TestComposeCurtinKernel(MAASServerTestCase): |
557 | + |
558 | + def test__returns_null_kernel(self): |
559 | + node = factory.make_Node() |
560 | + self.assertEqual(node.hwe_kernel, None) |
561 | + kernel_preseed = compose_curtin_kernel_preseed(node) |
562 | + self.assertEqual(kernel_preseed, []) |
563 | + |
564 | + def test__returns_set_kernel(self): |
565 | + self.patch( |
566 | + BootResource.objects, 'get_kpackage_for_node').return_value = ( |
567 | + 'linux-image-generic-lts-vivid') |
568 | + node = factory.make_Node(hwe_kernel='hwe-v') |
569 | + self.assertEqual(node.hwe_kernel, 'hwe-v') |
570 | + kernel_preseed = compose_curtin_kernel_preseed(node) |
571 | + self.assertEqual(kernel_preseed, |
572 | + ['kernel:\n' + |
573 | + ' mapping: {}\n' + |
574 | + ' package: linux-image-generic-lts-vivid\n' |
575 | + ]) |
576 | + |
577 | + |
578 | class TestComposeCurtinNetworkPreseed(MAASServerTestCase): |
579 | |
580 | def test__returns_list_of_yaml_strings(self): |
I think this is intended to fix bug 1473167 ?
If so, when you commit you can:
bzr commit --fixes lp:1473167
then when you push, launchpad will automagically link the bug to this review.