Merge lp:~newell-jensen/maas/move-get-boot-purpose-to-node-model into lp:~maas-committers/maas/trunk

Proposed by Newell Jensen
Status: Merged
Approved by: Newell Jensen
Approved revision: no longer in the source branch.
Merged at revision: 3070
Proposed branch: lp:~newell-jensen/maas/move-get-boot-purpose-to-node-model
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 284 lines (+84/-90)
4 files modified
src/maasserver/api/pxeconfig.py (+5/-36)
src/maasserver/api/tests/test_pxeconfig.py (+4/-54)
src/maasserver/models/node.py (+32/-0)
src/maasserver/models/tests/test_node.py (+43/-0)
To merge this branch: bzr merge lp:~newell-jensen/maas/move-get-boot-purpose-to-node-model
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Approve
Review via email: mp+235736@code.launchpad.net

Commit message

Moving get_boot_purpose from pxeconfig to the node model.

Description of the change

This change is so that we can get the boot purpose from the node itself instead of from pxeconfig. This work is related to:

https://bugs.launchpad.net/maas/+bug/1371122

Ultimately these changes are so that we can get PXE Request events without WindowsBootMethod hitting the API for every file, as mentioned in the bug.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Looking good. A bit of a shame to see the Node model module continue to grow: we've found the edge of manageability for a single source file to lie somewhere near the thousand-line mark. But yes, this code does look as if it belongs there.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/api/pxeconfig.py'
2--- src/maasserver/api/pxeconfig.py 2014-09-18 03:15:07 +0000
3+++ src/maasserver/api/pxeconfig.py 2014-09-24 02:34:00 +0000
4@@ -27,10 +27,7 @@
5 get_optional_param,
6 )
7 from maasserver.clusterrpc.boot_images import get_boot_images_for
8-from maasserver.enum import (
9- NODE_STATUS,
10- PRESEED_TYPE,
11- )
12+from maasserver.enum import NODE_STATUS
13 from maasserver.models import (
14 BootResource,
15 Config,
16@@ -40,7 +37,6 @@
17 from maasserver.preseed import (
18 compose_enlistment_preseed_url,
19 compose_preseed_url,
20- get_preseed_type_for,
21 )
22 from maasserver.server_address import get_maas_facing_server_address
23 from maasserver.third_party_drivers import get_third_party_driver
24@@ -82,36 +78,6 @@
25 return macaddress.node if macaddress else None
26
27
28-def get_boot_purpose(node):
29- """Return a suitable "purpose" for this boot, e.g. "install"."""
30- # XXX: allenap bug=1031406 2012-07-31: The boot purpose is still in
31- # flux. It may be that there will just be an "ephemeral" environment and
32- # an "install" environment, and the differing behaviour between, say,
33- # enlistment and commissioning - both of which will use the "ephemeral"
34- # environment - will be governed by varying the preseed or PXE
35- # configuration.
36- if node is None:
37- # This node is enlisting, for which we use a commissioning image.
38- return "commissioning"
39- elif node.status == NODE_STATUS.COMMISSIONING:
40- # It is commissioning.
41- return "commissioning"
42- elif node.status == NODE_STATUS.DEPLOYING:
43- # Install the node if netboot is enabled, otherwise boot locally.
44- if node.netboot:
45- preseed_type = get_preseed_type_for(node)
46- if preseed_type == PRESEED_TYPE.CURTIN:
47- return "xinstall"
48- else:
49- return "install"
50- else:
51- return "local"
52- elif node.status == NODE_STATUS.DEPLOYED:
53- return "local"
54- else:
55- return "poweroff"
56-
57-
58 def get_boot_image(
59 nodegroup, osystem, architecture, subarchitecture, series, purpose):
60 """Obtain the first available boot image for this cluster for the given
61@@ -214,7 +180,10 @@
62
63 # If we are booting with "xinstall", then we should always return the
64 # commissioning operating system and distro_series.
65- purpose = get_boot_purpose(node)
66+ if node is None:
67+ purpose = "commissioning" # enlistment
68+ else:
69+ purpose = node.get_boot_purpose()
70 if purpose == "xinstall":
71 osystem = Config.objects.get_config('commissioning_osystem')
72 series = Config.objects.get_config('commissioning_distro_series')
73
74=== modified file 'src/maasserver/api/tests/test_pxeconfig.py'
75--- src/maasserver/api/tests/test_pxeconfig.py 2014-09-20 15:42:12 +0000
76+++ src/maasserver/api/tests/test_pxeconfig.py 2014-09-24 02:34:00 +0000
77@@ -28,12 +28,10 @@
78 from maasserver.api.pxeconfig import (
79 find_nodegroup_for_pxeconfig_request,
80 get_boot_image,
81- get_boot_purpose,
82 )
83 from maasserver.clusterrpc.testing.boot_images import make_rpc_boot_image
84 from maasserver.enum import (
85 BOOT_RESOURCE_TYPE,
86- NODE_BOOT,
87 NODE_STATUS,
88 NODEGROUPINTERFACE_MANAGEMENT,
89 )
90@@ -47,7 +45,6 @@
91 )
92 from maasserver.testing.architecture import make_usable_architecture
93 from maasserver.testing.factory import factory
94-from maasserver.testing.osystems import make_usable_osystem
95 from maasserver.testing.testcase import MAASServerTestCase
96 from maastesting.fakemethod import FakeMethod
97 from mock import sentinel
98@@ -367,60 +364,13 @@
99 json.loads(response.content)["preseed_url"],
100 StartsWith(ng_url))
101
102- def test_get_boot_purpose_unknown_node(self):
103- # A node that's not yet known to MAAS is assumed to be enlisting,
104- # which uses a "commissioning" image.
105- self.assertEqual("commissioning", get_boot_purpose(None))
106-
107- def test_get_boot_purpose_known_node(self):
108- # The following table shows the expected boot "purpose" for each set
109- # of node parameters.
110- options = [
111- ("poweroff", {"status": NODE_STATUS.NEW}),
112- ("commissioning", {"status": NODE_STATUS.COMMISSIONING}),
113- ("poweroff", {"status": NODE_STATUS.FAILED_COMMISSIONING}),
114- ("poweroff", {"status": NODE_STATUS.MISSING}),
115- ("poweroff", {"status": NODE_STATUS.READY}),
116- ("poweroff", {"status": NODE_STATUS.RESERVED}),
117- ("install", {"status": NODE_STATUS.DEPLOYING, "netboot": True}),
118- ("xinstall", {"status": NODE_STATUS.DEPLOYING, "netboot": True}),
119- ("local", {"status": NODE_STATUS.DEPLOYING, "netboot": False}),
120- ("local", {"status": NODE_STATUS.DEPLOYED}),
121- ("poweroff", {"status": NODE_STATUS.RETIRED}),
122- ]
123- node = factory.make_Node(boot_type=NODE_BOOT.DEBIAN)
124- mock_get_boot_images_for = self.patch(
125- preseed_module, 'get_boot_images_for')
126- for purpose, parameters in options:
127- boot_image = make_rpc_boot_image(purpose=purpose)
128- mock_get_boot_images_for.return_value = [boot_image]
129- if purpose == "xinstall":
130- node.boot_type = NODE_BOOT.FASTPATH
131- for name, value in parameters.items():
132- setattr(node, name, value)
133- self.assertEqual(purpose, get_boot_purpose(node))
134-
135- def test_get_boot_purpose_osystem_no_xinstall_support(self):
136- osystem = make_usable_osystem(self)
137- release = osystem['default_release']
138- node = factory.make_Node(
139- status=NODE_STATUS.DEPLOYING, netboot=True,
140- osystem=osystem['name'], distro_series=release,
141- boot_type=NODE_BOOT.FASTPATH)
142- boot_image = make_rpc_boot_image(purpose='install')
143- self.patch(
144- preseed_module, 'get_boot_images_for').return_value = [boot_image]
145- self.assertEqual('install', get_boot_purpose(node))
146-
147- def test_pxeconfig_uses_boot_purpose(self):
148- fake_boot_purpose = factory.make_name("purpose")
149- self.patch(
150- pxeconfig_module, "get_boot_purpose"
151- ).return_value = fake_boot_purpose
152+ def test_pxeconfig_uses_boot_purpose_enlistment(self):
153+ # test that purpose is set to "commissioning" for
154+ # enlistment (when node is None).
155 params = self.get_default_params()
156 response = self.client.get(reverse('pxeconfig'), params)
157 self.assertEqual(
158- fake_boot_purpose,
159+ "commissioning",
160 json.loads(response.content)["purpose"])
161
162 def test_pxeconfig_returns_fs_host_as_cluster_controller(self):
163
164=== modified file 'src/maasserver/models/node.py'
165--- src/maasserver/models/node.py 2014-09-23 18:04:10 +0000
166+++ src/maasserver/models/node.py 2014-09-24 02:34:00 +0000
167@@ -70,6 +70,7 @@
168 NODEGROUPINTERFACE_MANAGEMENT,
169 POWER_STATE,
170 POWER_STATE_CHOICES,
171+ PRESEED_TYPE,
172 )
173 from maasserver.exceptions import (
174 NodeStateViolation,
175@@ -97,6 +98,7 @@
176 is_failed_status,
177 NODE_TRANSITIONS,
178 )
179+from maasserver.preseed import get_preseed_type_for
180 from maasserver.rpc import getClientFor
181 from maasserver.utils import (
182 get_db_state,
183@@ -1331,3 +1333,33 @@
184 # Return a list instead of yielding mappings as they're ready
185 # because it's all-or-nothing (hence the atomic context).
186 return mappings
187+
188+ def get_boot_purpose(self):
189+ """
190+ Return a suitable "purpose" for this boot, e.g. "install".
191+ """
192+ # XXX: allenap bug=1031406 2012-07-31: The boot purpose is
193+ # still in flux. It may be that there will just be an
194+ # "ephemeral" environment and an "install" environment, and
195+ # the differing behaviour between, say, enlistment and
196+ # commissioning - both of which will use the "ephemeral"
197+ # environment - will be governed by varying the preseed or PXE
198+ # configuration.
199+ if self.status == NODE_STATUS.COMMISSIONING:
200+ # It is commissioning.
201+ return "commissioning"
202+ elif self.status == NODE_STATUS.DEPLOYING:
203+ # Install the node if netboot is enabled,
204+ # otherwise boot locally.
205+ if self.netboot:
206+ preseed_type = get_preseed_type_for(self)
207+ if preseed_type == PRESEED_TYPE.CURTIN:
208+ return "xinstall"
209+ else:
210+ return "install"
211+ else:
212+ return "local"
213+ elif self.status == NODE_STATUS.DEPLOYED:
214+ return "local"
215+ else:
216+ return "poweroff"
217
218=== modified file 'src/maasserver/models/tests/test_node.py'
219--- src/maasserver/models/tests/test_node.py 2014-09-23 18:04:10 +0000
220+++ src/maasserver/models/tests/test_node.py 2014-09-24 02:34:00 +0000
221@@ -23,7 +23,9 @@
222
223 import crochet
224 from django.core.exceptions import ValidationError
225+from maasserver import preseed as preseed_module
226 from maasserver.clusterrpc.power_parameters import get_power_types
227+from maasserver.clusterrpc.testing.boot_images import make_rpc_boot_image
228 from maasserver.dns import config as dns_config
229 from maasserver.enum import (
230 IPADDRESS_TYPE,
231@@ -68,6 +70,7 @@
232 )
233 from maasserver.testing.factory import factory
234 from maasserver.testing.orm import reload_object
235+from maasserver.testing.osystems import make_usable_osystem
236 from maasserver.testing.testcase import MAASServerTestCase
237 from maasserver.utils import ignore_unused
238 from maastesting.djangotestcase import count_queries
239@@ -1337,6 +1340,46 @@
240 node = reload_object(node)
241 self.assertEqual(status, node.status)
242
243+ def test_get_boot_purpose_known_node(self):
244+ # The following table shows the expected boot "purpose" for each set
245+ # of node parameters.
246+ options = [
247+ ("poweroff", {"status": NODE_STATUS.NEW}),
248+ ("commissioning", {"status": NODE_STATUS.COMMISSIONING}),
249+ ("poweroff", {"status": NODE_STATUS.FAILED_COMMISSIONING}),
250+ ("poweroff", {"status": NODE_STATUS.MISSING}),
251+ ("poweroff", {"status": NODE_STATUS.READY}),
252+ ("poweroff", {"status": NODE_STATUS.RESERVED}),
253+ ("install", {"status": NODE_STATUS.DEPLOYING, "netboot": True}),
254+ ("xinstall", {"status": NODE_STATUS.DEPLOYING, "netboot": True}),
255+ ("local", {"status": NODE_STATUS.DEPLOYING, "netboot": False}),
256+ ("local", {"status": NODE_STATUS.DEPLOYED}),
257+ ("poweroff", {"status": NODE_STATUS.RETIRED}),
258+ ]
259+ node = factory.make_Node(boot_type=NODE_BOOT.DEBIAN)
260+ mock_get_boot_images_for = self.patch(
261+ preseed_module, 'get_boot_images_for')
262+ for purpose, parameters in options:
263+ boot_image = make_rpc_boot_image(purpose=purpose)
264+ mock_get_boot_images_for.return_value = [boot_image]
265+ if purpose == "xinstall":
266+ node.boot_type = NODE_BOOT.FASTPATH
267+ for name, value in parameters.items():
268+ setattr(node, name, value)
269+ self.assertEqual(purpose, node.get_boot_purpose())
270+
271+ def test_get_boot_purpose_osystem_no_xinstall_support(self):
272+ osystem = make_usable_osystem(self)
273+ release = osystem['default_release']
274+ node = factory.make_Node(
275+ status=NODE_STATUS.DEPLOYING, netboot=True,
276+ osystem=osystem['name'], distro_series=release,
277+ boot_type=NODE_BOOT.FASTPATH)
278+ boot_image = make_rpc_boot_image(purpose='install')
279+ self.patch(
280+ preseed_module, 'get_boot_images_for').return_value = [boot_image]
281+ self.assertEqual('install', node.get_boot_purpose())
282+
283
284 class NodeRoutersTest(MAASServerTestCase):
285