Merge lp:~rvb/maas/poweroff-redux into lp:~maas-committers/maas/trunk
- poweroff-redux
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Raphaël Badin |
Approved revision: | no longer in the source branch. |
Merged at revision: | 3400 |
Proposed branch: | lp:~rvb/maas/poweroff-redux |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
499 lines (+174/-82) 12 files modified
etc/maas/templates/commissioning-user-data/user_data_poweroff.template (+28/-0) src/maasserver/api/pxeconfig.py (+7/-12) src/maasserver/api/tests/test_pxeconfig.py (+9/-4) src/maasserver/models/node.py (+2/-2) src/maasserver/preseed.py (+10/-1) src/maasserver/tests/test_preseed.py (+25/-7) src/metadataserver/api.py (+8/-2) src/metadataserver/tests/test_api.py (+19/-3) src/metadataserver/user_data/poweroff.py (+34/-0) src/metadataserver/user_data/tests/test_poweroff.py (+31/-0) src/provisioningserver/boot/__init__.py (+0/-16) src/provisioningserver/boot/tests/test_boot.py (+1/-35) |
To merge this branch: | bzr merge lp:~rvb/maas/poweroff-redux |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+243700@code.launchpad.net |
Commit message
In order to power a node off, don't use poweroff.
Description of the change
I tested this manually on my NUCs and I got it through the CI (http://
Once this lands, we can update the CI code to use the NUCs we have in the CI lab and that we couldn't use so far because of the bug this branch is fixing.
Raphaël Badin (rvb) wrote : | # |
Thanks for the review. I think I've addressed most of your comments but I confess I didn't do a full refactoring of the get_boot_purpose() code.
MAAS Lander (maas-lander) wrote : | # |
There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.
Preview Diff
1 | === added file 'etc/maas/templates/commissioning-user-data/user_data_poweroff.template' |
2 | --- etc/maas/templates/commissioning-user-data/user_data_poweroff.template 1970-01-01 00:00:00 +0000 |
3 | +++ etc/maas/templates/commissioning-user-data/user_data_poweroff.template 2014-12-05 16:13:51 +0000 |
4 | @@ -0,0 +1,28 @@ |
5 | +#!/bin/sh |
6 | + |
7 | +#### script setup ###### |
8 | +PATH="$BIN_D:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" |
9 | + |
10 | +write_poweroff_job() { |
11 | + cat >/etc/init/maas-poweroff.conf <<EOF |
12 | + description "Power-off when MAAS task is done" |
13 | + start on stopped cloud-final |
14 | + console output |
15 | + task |
16 | + script |
17 | + [ ! -e /tmp/block-poweroff ] || exit 0 |
18 | + /sbin/poweroff |
19 | + end script |
20 | +EOF |
21 | + # reload required due to lack of inotify in overlayfs (LP: #882147) |
22 | + initctl reload-configuration |
23 | +} |
24 | + |
25 | +main() { |
26 | + write_poweroff_job |
27 | + |
28 | + echo "Powering node off." |
29 | +} |
30 | + |
31 | +main |
32 | +exit |
33 | |
34 | === modified file 'src/maasserver/api/pxeconfig.py' |
35 | --- src/maasserver/api/pxeconfig.py 2014-11-28 15:38:33 +0000 |
36 | +++ src/maasserver/api/pxeconfig.py 2014-12-05 16:13:51 +0000 |
37 | @@ -226,24 +226,19 @@ |
38 | osystem = Config.objects.get_config('commissioning_osystem') |
39 | series = Config.objects.get_config('commissioning_distro_series') |
40 | |
41 | - # If we are powering off the node, then we only need to return enough |
42 | - # to turn the machine off. Calculation of the label, kernel parameters, |
43 | - # server address, and cluster address is not needed. |
44 | if purpose == 'poweroff': |
45 | - params = KernelParameters( |
46 | - osystem="", arch=arch, subarch=subarch, release="", |
47 | - label="", purpose=purpose, hostname=hostname, domain=domain, |
48 | - preseed_url="", log_host="", fs_host="", extra_opts="") |
49 | - return HttpResponse( |
50 | - json.dumps(params._asdict()), |
51 | - content_type="application/json") |
52 | + # In order to power the node off, we need to get it booted in the |
53 | + # commissioning environment and issue a `poweroff` command. |
54 | + boot_purpose = 'commissioning' |
55 | + else: |
56 | + boot_purpose = purpose |
57 | |
58 | # We use as our default label the label of the most recent image for |
59 | # the criteria we've assembled above. If there is no latest image |
60 | # (which should never happen in reality but may happen in tests), we |
61 | # fall back to using 'no-such-image' as our default. |
62 | latest_image = get_boot_image( |
63 | - nodegroup, osystem, arch, subarch, series, purpose) |
64 | + nodegroup, osystem, arch, subarch, series, boot_purpose) |
65 | if latest_image is None: |
66 | # XXX 2014-03-18 gmb bug=1294131: |
67 | # We really ought to raise an exception here so that client |
68 | @@ -291,7 +286,7 @@ |
69 | |
70 | params = KernelParameters( |
71 | osystem=osystem, arch=arch, subarch=subarch, release=series, |
72 | - label=label, purpose=purpose, hostname=hostname, domain=domain, |
73 | + label=label, purpose=boot_purpose, hostname=hostname, domain=domain, |
74 | preseed_url=preseed_url, log_host=server_address, |
75 | fs_host=cluster_address, extra_opts=extra_kernel_opts) |
76 | |
77 | |
78 | === modified file 'src/maasserver/api/tests/test_pxeconfig.py' |
79 | --- src/maasserver/api/tests/test_pxeconfig.py 2014-11-28 15:44:21 +0000 |
80 | +++ src/maasserver/api/tests/test_pxeconfig.py 2014-12-05 16:13:51 +0000 |
81 | @@ -419,21 +419,26 @@ |
82 | pxe_config = self.get_pxeconfig(params) |
83 | self.assertEqual(None, pxe_config['extra_opts']) |
84 | |
85 | - def test_pxeconfig_returns_poweroff_for_insane_state(self): |
86 | + def test_pxeconfig_returns_commissioning_for_insane_state(self): |
87 | mac = factory.make_MACAddress_with_Node() |
88 | params = self.get_default_params() |
89 | params['mac'] = mac.mac_address |
90 | pxe_config = self.get_pxeconfig(params) |
91 | - self.assertEqual('poweroff', pxe_config['purpose']) |
92 | + # The 'purpose' of the PXE config is 'commissioning' here |
93 | + # even if the 'purpose' returned by node.get_boot_purpose |
94 | + # is 'poweroff' because MAAS needs to bring the machine |
95 | + # up in a commissioning environment in order to power |
96 | + # the machine down. |
97 | + self.assertEqual('commissioning', pxe_config['purpose']) |
98 | |
99 | - def test_pxeconfig_returns_poweroff_for_ready_node(self): |
100 | + def test_pxeconfig_returns_commissioning_for_ready_node(self): |
101 | mac = factory.make_MACAddress_with_Node() |
102 | mac.node.status = NODE_STATUS.READY |
103 | mac.node.save() |
104 | params = self.get_default_params() |
105 | params['mac'] = mac.mac_address |
106 | pxe_config = self.get_pxeconfig(params) |
107 | - self.assertEqual('poweroff', pxe_config['purpose']) |
108 | + self.assertEqual('commissioning', pxe_config['purpose']) |
109 | |
110 | def test_pxeconfig_returns_image_subarch_not_node_subarch(self): |
111 | # In the scenario such as deploying trusty on an hwe-s subarch |
112 | |
113 | === modified file 'src/maasserver/models/node.py' |
114 | --- src/maasserver/models/node.py 2014-11-26 10:11:28 +0000 |
115 | +++ src/maasserver/models/node.py 2014-12-05 16:13:51 +0000 |
116 | @@ -1396,8 +1396,8 @@ |
117 | # otherwise boot locally. |
118 | if self.netboot: |
119 | # Avoid circular imports. |
120 | - from maasserver.preseed import get_preseed_type_for |
121 | - preseed_type = get_preseed_type_for(self) |
122 | + from maasserver.preseed import get_deploying_preseed_type_for |
123 | + preseed_type = get_deploying_preseed_type_for(self) |
124 | if preseed_type == PRESEED_TYPE.CURTIN: |
125 | return "xinstall" |
126 | else: |
127 | |
128 | === modified file 'src/maasserver/preseed.py' |
129 | --- src/maasserver/preseed.py 2014-11-07 13:16:58 +0000 |
130 | +++ src/maasserver/preseed.py 2014-12-05 16:13:51 +0000 |
131 | @@ -338,8 +338,17 @@ |
132 | using the default installer but there is no boot image that supports |
133 | that method then it will boot using the fast-path installer. |
134 | """ |
135 | - if node.status in COMMISSIONING_LIKE_STATUSES: |
136 | + is_commissioning_preseed = ( |
137 | + node.status in COMMISSIONING_LIKE_STATUSES or |
138 | + node.get_boot_purpose() == 'poweroff' |
139 | + ) |
140 | + if is_commissioning_preseed: |
141 | return PRESEED_TYPE.COMMISSIONING |
142 | + else: |
143 | + return get_deploying_preseed_type_for(node) |
144 | + |
145 | + |
146 | +def get_deploying_preseed_type_for(node): |
147 | if node.boot_type == NODE_BOOT.FASTPATH: |
148 | purpose_order = ['xinstall', 'install'] |
149 | elif node.boot_type == NODE_BOOT.DEBIAN: |
150 | |
151 | === modified file 'src/maasserver/tests/test_preseed.py' |
152 | --- src/maasserver/tests/test_preseed.py 2014-10-09 16:32:04 +0000 |
153 | +++ src/maasserver/tests/test_preseed.py 2014-12-05 16:13:51 +0000 |
154 | @@ -666,7 +666,8 @@ |
155 | self.return_windows_specific_preseed_data() |
156 | node = factory.make_Node( |
157 | nodegroup=self.rpc_nodegroup, osystem='windows', |
158 | - architecture='amd64/generic', distro_series=self.release) |
159 | + architecture='amd64/generic', distro_series=self.release, |
160 | + status=NODE_STATUS.DEPLOYING) |
161 | self.configure_get_boot_images_for_node(node, 'install') |
162 | preseed = render_preseed( |
163 | node, '', osystem='windows', release=self.release) |
164 | @@ -1207,29 +1208,42 @@ |
165 | PRESEED_TYPE.COMMISSIONING, get_preseed_type_for(node)) |
166 | |
167 | def test_get_preseed_type_for_default(self): |
168 | - node = factory.make_Node(boot_type=NODE_BOOT.DEBIAN) |
169 | + node = factory.make_Node( |
170 | + boot_type=NODE_BOOT.DEBIAN, status=NODE_STATUS.DEPLOYING) |
171 | self.configure_get_boot_images_for_node(node, 'install') |
172 | self.assertEqual( |
173 | PRESEED_TYPE.DEFAULT, get_preseed_type_for(node)) |
174 | |
175 | def test_get_preseed_type_for_curtin(self): |
176 | - node = factory.make_Node(boot_type=NODE_BOOT.FASTPATH) |
177 | + node = factory.make_Node( |
178 | + boot_type=NODE_BOOT.FASTPATH, status=NODE_STATUS.DEPLOYING) |
179 | self.configure_get_boot_images_for_node(node, 'xinstall') |
180 | self.assertEqual( |
181 | PRESEED_TYPE.CURTIN, get_preseed_type_for(node)) |
182 | |
183 | def test_get_preseed_type_for_default_when_curtin_not_supported(self): |
184 | - node = factory.make_Node(boot_type=NODE_BOOT.FASTPATH) |
185 | + node = factory.make_Node( |
186 | + boot_type=NODE_BOOT.FASTPATH, status=NODE_STATUS.DEPLOYING) |
187 | self.configure_get_boot_images_for_node(node, 'install') |
188 | self.assertEqual( |
189 | PRESEED_TYPE.DEFAULT, get_preseed_type_for(node)) |
190 | |
191 | def test_get_preseed_type_for_curtin_when_default_not_supported(self): |
192 | - node = factory.make_Node(boot_type=NODE_BOOT.DEBIAN) |
193 | + node = factory.make_Node( |
194 | + boot_type=NODE_BOOT.DEBIAN, status=NODE_STATUS.DEPLOYING) |
195 | self.configure_get_boot_images_for_node(node, 'xinstall') |
196 | self.assertEqual( |
197 | PRESEED_TYPE.CURTIN, get_preseed_type_for(node)) |
198 | |
199 | + def test_get_preseed_type_for_poweroff(self): |
200 | + # A 'ready' node isn't supposed to be powered on and thus |
201 | + # will get a 'commissioning' preseed in order to be powered |
202 | + # down. |
203 | + node = factory.make_Node( |
204 | + boot_type=NODE_BOOT.DEBIAN, status=NODE_STATUS.READY) |
205 | + self.assertEqual( |
206 | + PRESEED_TYPE.COMMISSIONING, get_preseed_type_for(node)) |
207 | + |
208 | |
209 | class TestRenderPreseedArchives( |
210 | PreseedRPCMixin, BootImageHelperMixin, MAASServerTestCase): |
211 | @@ -1239,10 +1253,12 @@ |
212 | nodes = [ |
213 | factory.make_Node( |
214 | nodegroup=self.rpc_nodegroup, |
215 | + status=NODE_STATUS.DEPLOYING, |
216 | architecture=make_usable_architecture( |
217 | self, arch_name="i386", subarch_name="generic")), |
218 | factory.make_Node( |
219 | nodegroup=self.rpc_nodegroup, |
220 | + status=NODE_STATUS.DEPLOYING, |
221 | architecture=make_usable_architecture( |
222 | self, arch_name="amd64", subarch_name="generic")), |
223 | ] |
224 | @@ -1314,14 +1330,16 @@ |
225 | |
226 | def test_get_preseed_returns_default_preseed(self): |
227 | node = factory.make_Node( |
228 | - nodegroup=self.rpc_nodegroup, boot_type=NODE_BOOT.DEBIAN) |
229 | + nodegroup=self.rpc_nodegroup, boot_type=NODE_BOOT.DEBIAN, |
230 | + status=NODE_STATUS.DEPLOYING) |
231 | self.configure_get_boot_images_for_node(node, 'install') |
232 | preseed = get_preseed(node) |
233 | self.assertIn('preseed/late_command', preseed) |
234 | |
235 | def test_get_preseed_returns_curtin_preseed(self): |
236 | node = factory.make_Node( |
237 | - nodegroup=self.rpc_nodegroup, boot_type=NODE_BOOT.FASTPATH) |
238 | + nodegroup=self.rpc_nodegroup, boot_type=NODE_BOOT.FASTPATH, |
239 | + status=NODE_STATUS.DEPLOYING) |
240 | self.configure_get_boot_images_for_node(node, 'xinstall') |
241 | preseed = get_preseed(node) |
242 | curtin_url = reverse('curtin-metadata') |
243 | |
244 | === modified file 'src/metadataserver/api.py' |
245 | --- src/metadataserver/api.py 2014-11-12 03:27:06 +0000 |
246 | +++ src/metadataserver/api.py 2014-12-05 16:13:51 +0000 |
247 | @@ -78,6 +78,7 @@ |
248 | from metadataserver.models.commissioningscript import ( |
249 | BUILTIN_COMMISSIONING_SCRIPTS, |
250 | ) |
251 | +from metadataserver.user_data import poweroff |
252 | from piston.utils import rc |
253 | from provisioningserver.events import ( |
254 | EVENT_DETAILS, |
255 | @@ -424,9 +425,14 @@ |
256 | # off to a user. |
257 | if node.status == NODE_STATUS.DEPLOYING: |
258 | node.end_deployment() |
259 | + # If this node is supposed to be powered off, serve the |
260 | + # 'poweroff' userdata. |
261 | + if node.get_boot_purpose() == 'poweroff': |
262 | + user_data = poweroff.generate_user_data(node=node) |
263 | + else: |
264 | + user_data = NodeUserData.objects.get_user_data(node) |
265 | return HttpResponse( |
266 | - NodeUserData.objects.get_user_data(node), |
267 | - mimetype='application/octet-stream') |
268 | + user_data, mimetype='application/octet-stream') |
269 | except NodeUserData.DoesNotExist: |
270 | logger.info( |
271 | "No user data registered for node named %s" % node.hostname) |
272 | |
273 | === modified file 'src/metadataserver/tests/test_api.py' |
274 | --- src/metadataserver/tests/test_api.py 2014-11-25 13:06:34 +0000 |
275 | +++ src/metadataserver/tests/test_api.py 2014-12-05 16:13:51 +0000 |
276 | @@ -62,6 +62,7 @@ |
277 | make_list_response, |
278 | make_text_response, |
279 | MetaDataHandler, |
280 | + poweroff as api_poweroff, |
281 | UnknownMetadataVersion, |
282 | ) |
283 | from metadataserver.models import ( |
284 | @@ -383,7 +384,7 @@ |
285 | """Tests for the metadata user-data API endpoint.""" |
286 | |
287 | def test_user_data_view_returns_binary_data(self): |
288 | - node = factory.make_Node() |
289 | + node = factory.make_Node(status=NODE_STATUS.COMMISSIONING) |
290 | NodeUserData.objects.set_user_data(node, sample_binary_data) |
291 | client = make_node_client(node) |
292 | response = client.get(reverse('metadata-user-data', args=['latest'])) |
293 | @@ -393,8 +394,22 @@ |
294 | (httplib.OK, sample_binary_data), |
295 | (response.status_code, response.content)) |
296 | |
297 | + def test_poweroff_user_data_returned_if_unexpected_status(self): |
298 | + node = factory.make_Node(status=NODE_STATUS.READY) |
299 | + NodeUserData.objects.set_user_data(node, sample_binary_data) |
300 | + client = make_node_client(node) |
301 | + user_data = factory.make_name('user data').encode("ascii") |
302 | + self.patch(api_poweroff, 'generate_user_data').return_value = user_data |
303 | + response = client.get(reverse('metadata-user-data', args=['latest'])) |
304 | + self.assertEqual('application/octet-stream', response['Content-Type']) |
305 | + self.assertIsInstance(response.content, bytes) |
306 | + self.assertEqual( |
307 | + (httplib.OK, user_data), |
308 | + (response.status_code, response.content)) |
309 | + |
310 | def test_user_data_for_node_without_user_data_returns_not_found(self): |
311 | - client = make_node_client() |
312 | + client = make_node_client( |
313 | + factory.make_Node(status=NODE_STATUS.COMMISSIONING)) |
314 | response = client.get(reverse('metadata-user-data', args=['latest'])) |
315 | self.assertEqual(httplib.NOT_FOUND, response.status_code) |
316 | |
317 | @@ -938,7 +953,8 @@ |
318 | (response.status_code, response.content)) |
319 | |
320 | def test_api_retrieves_node_userdata_by_mac(self): |
321 | - mac = factory.make_MACAddress_with_Node() |
322 | + mac = factory.make_MACAddress_with_Node( |
323 | + node=factory.make_Node(status=NODE_STATUS.COMMISSIONING)) |
324 | user_data = factory.make_string().encode('ascii') |
325 | NodeUserData.objects.set_user_data(mac.node, user_data) |
326 | url = reverse( |
327 | |
328 | === added file 'src/metadataserver/user_data/poweroff.py' |
329 | --- src/metadataserver/user_data/poweroff.py 1970-01-01 00:00:00 +0000 |
330 | +++ src/metadataserver/user_data/poweroff.py 2014-12-05 16:13:51 +0000 |
331 | @@ -0,0 +1,34 @@ |
332 | +# Copyright 2014 Canonical Ltd. This software is licensed under the |
333 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
334 | + |
335 | +"""Poweroff userdata generation.""" |
336 | + |
337 | +from __future__ import ( |
338 | + absolute_import, |
339 | + print_function, |
340 | + unicode_literals, |
341 | + ) |
342 | + |
343 | +str = None |
344 | + |
345 | +__metaclass__ = type |
346 | +__all__ = [ |
347 | + "generate_user_data", |
348 | +] |
349 | + |
350 | +from metadataserver.user_data.snippets import get_userdata_template_dir |
351 | +from metadataserver.user_data.utils import ( |
352 | + generate_user_data as _generate_user_data, |
353 | + ) |
354 | + |
355 | + |
356 | +def generate_user_data(node): |
357 | + """Produce the poweroff script. |
358 | + |
359 | + :rtype: `bytes` |
360 | + """ |
361 | + userdata_dir = get_userdata_template_dir() |
362 | + result = _generate_user_data( |
363 | + node, userdata_dir, 'user_data_poweroff.template', |
364 | + 'user_data_config.template') |
365 | + return result |
366 | |
367 | === added file 'src/metadataserver/user_data/tests/test_poweroff.py' |
368 | --- src/metadataserver/user_data/tests/test_poweroff.py 1970-01-01 00:00:00 +0000 |
369 | +++ src/metadataserver/user_data/tests/test_poweroff.py 2014-12-05 16:13:51 +0000 |
370 | @@ -0,0 +1,31 @@ |
371 | +# Copyright 2014 Canonical Ltd. This software is licensed under the |
372 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
373 | + |
374 | +"""Test generation of poweroff user data.""" |
375 | + |
376 | +from __future__ import ( |
377 | + absolute_import, |
378 | + print_function, |
379 | + unicode_literals, |
380 | + ) |
381 | + |
382 | +str = None |
383 | + |
384 | +__metaclass__ = type |
385 | +__all__ = [] |
386 | + |
387 | +from maasserver.testing.factory import factory |
388 | +from maasserver.testing.testcase import MAASServerTestCase |
389 | +from metadataserver.user_data.poweroff import generate_user_data |
390 | +from testtools.matchers import ContainsAll |
391 | + |
392 | + |
393 | +class TestPoweroffUserData(MAASServerTestCase): |
394 | + |
395 | + def test_generate_user_data_produces_poweroff_script(self): |
396 | + node = factory.make_Node() |
397 | + self.assertThat( |
398 | + generate_user_data(node), ContainsAll({ |
399 | + 'Powering node off', |
400 | + 'poweroff', |
401 | + })) |
402 | |
403 | === modified file 'src/provisioningserver/boot/__init__.py' |
404 | --- src/provisioningserver/boot/__init__.py 2014-11-17 11:56:49 +0000 |
405 | +++ src/provisioningserver/boot/__init__.py 2014-12-05 16:13:51 +0000 |
406 | @@ -26,7 +26,6 @@ |
407 | from io import BytesIO |
408 | from os import path |
409 | |
410 | -from provisioningserver import config |
411 | from provisioningserver.boot.tftppath import compose_image_path |
412 | from provisioningserver.kernel_opts import compose_kernel_command_line |
413 | from provisioningserver.utils import locate_config |
414 | @@ -107,18 +106,6 @@ |
415 | return find_mac_via_arp(remote_host) |
416 | |
417 | |
418 | -def compose_poweroff_command(): |
419 | - """Composes the poweroff command depending on the version of |
420 | - syslinux that is avaliable in the tftproot.""" |
421 | - com32_path = path.join( |
422 | - config.BOOT_RESOURCES_STORAGE, "current", "syslinux", "poweroff.c32") |
423 | - if path.exists(com32_path): |
424 | - # syslinux 6 uses a com32 module for poweroff |
425 | - return "COM32 /syslinux/poweroff.c32" |
426 | - # syslinux 4 uses the older type |
427 | - return "KERNEL /syslinux/poweroff.com" |
428 | - |
429 | - |
430 | class BootMethod: |
431 | """Skeleton for a boot method.""" |
432 | |
433 | @@ -245,9 +232,6 @@ |
434 | "kernel_path": kernel_path, |
435 | } |
436 | |
437 | - if kernel_params.purpose == 'poweroff': |
438 | - namespace['poweroff'] = compose_poweroff_command() |
439 | - |
440 | return namespace |
441 | |
442 | |
443 | |
444 | === modified file 'src/provisioningserver/boot/tests/test_boot.py' |
445 | --- src/provisioningserver/boot/tests/test_boot.py 2014-09-13 11:38:50 +0000 |
446 | +++ src/provisioningserver/boot/tests/test_boot.py 2014-12-05 16:13:51 +0000 |
447 | @@ -25,18 +25,13 @@ |
448 | MAASTwistedRunTest, |
449 | ) |
450 | import mock |
451 | -from provisioningserver import ( |
452 | - boot, |
453 | - config, |
454 | - ) |
455 | +from provisioningserver import boot |
456 | from provisioningserver.boot import ( |
457 | BootMethod, |
458 | BytesReader, |
459 | - compose_poweroff_command, |
460 | gen_template_filenames, |
461 | get_remote_mac, |
462 | ) |
463 | -from provisioningserver.tests.test_kernel_opts import make_kernel_parameters |
464 | import tempita |
465 | from twisted.internet.defer import inlineCallbacks |
466 | from twisted.python import context |
467 | @@ -155,32 +150,3 @@ |
468 | self.assertRaises( |
469 | IOError, method.get_template, |
470 | *factory.make_names("purpose", "arch", "subarch")) |
471 | - |
472 | - def test_compose_poweroff_command_for_syslinux_6(self): |
473 | - storage_dir = self.make_dir() |
474 | - syslinux_path = os.path.join(storage_dir, "current", "syslinux") |
475 | - os.makedirs(syslinux_path) |
476 | - factory.make_file(syslinux_path, "poweroff.c32", contents=None) |
477 | - self.patch(config, 'BOOT_RESOURCES_STORAGE', storage_dir) |
478 | - self.assertEqual( |
479 | - "COM32 /syslinux/poweroff.c32", |
480 | - compose_poweroff_command()) |
481 | - |
482 | - def test_compose_poweroff_command_for_syslinux_4(self): |
483 | - storage_dir = self.make_dir() |
484 | - syslinux_path = os.path.join(storage_dir, "current", "syslinux") |
485 | - os.makedirs(syslinux_path) |
486 | - factory.make_file(syslinux_path, "poweroff.com", contents=None) |
487 | - self.patch(config, 'BOOT_RESOURCES_STORAGE', storage_dir) |
488 | - self.assertEqual( |
489 | - "KERNEL /syslinux/poweroff.com", |
490 | - compose_poweroff_command()) |
491 | - |
492 | - def test_compose_template_namespace_includes_poweroff(self): |
493 | - fake_poweroff = factory.make_name('poweroff') |
494 | - self.patch( |
495 | - boot, 'compose_poweroff_command').return_value = fake_poweroff |
496 | - method = FakeBootMethod() |
497 | - kernel_params = make_kernel_parameters(purpose="poweroff") |
498 | - namespace = method.compose_template_namespace(kernel_params) |
499 | - self.assertEqual(fake_poweroff, namespace['poweroff']) |
Cool. Should work everywhere, as long as the machine can power itself off. Machines that can't aren't really MAAS's core audience (or peripheral either). Lots of comments but none of them are blockers.