Merge ~ltrager/maas:config_network_failed_testing into maas:master
- Git
- lp:~ltrager/maas
- config_network_failed_testing
- Merge into master
Proposed by
Lee Trager
Status: | Merged |
---|---|
Approved by: | Lee Trager |
Approved revision: | 2ef17cc18e3985070288bec89a9fe5da0fc6385b |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~ltrager/maas:config_network_failed_testing |
Merge into: | maas:master |
Diff against target: |
721 lines (+126/-111) 8 files modified
src/maasserver/api/interfaces.py (+4/-1) src/maasserver/api/machines.py (+12/-9) src/maasserver/api/tests/test_interfaces.py (+69/-87) src/maasserver/api/tests/test_machine.py (+15/-6) src/maasserver/forms/clone.py (+2/-1) src/maasserver/forms/tests/test_clone.py (+19/-6) src/maasserver/static/js/angular/controllers/node_details_networking.js (+3/-1) src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js (+2/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
MAAS Lander | Needs Fixing | ||
Review via email: mp+371552@code.launchpad.net |
Commit message
Allow network configuration to be changed when in FAILED_TESTING.
When network-validation fails the machine will be put into FAILED_TESTING.
The failure may be due to user configuration so allow users to change
configuration without overriding failed tests.
Description of the change
To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote : | # |
That's a lot of tests for such a simple change... I hate that!
review:
Approve
Revision history for this message
Adam Collard (adam-collard) : | # |
- 517f9d5... by Lee Trager
-
Merge branch 'master' into config_
network_ failed_ testing - 2ef17cc... by Lee Trager
-
adam-collard fixes
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/api/interfaces.py b/src/maasserver/api/interfaces.py |
2 | index 0232d08..099b50a 100644 |
3 | --- a/src/maasserver/api/interfaces.py |
4 | +++ b/src/maasserver/api/interfaces.py |
5 | @@ -98,7 +98,10 @@ INTERFACES_PREFETCH = [ |
6 | 'children_relationships__child__vlan'), |
7 | ] |
8 | |
9 | -ALLOWED_STATES = (NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN) |
10 | +ALLOWED_STATES = ( |
11 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING, |
12 | + NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN, |
13 | +) |
14 | |
15 | |
16 | def raise_error_for_invalid_state_on_allocated_operations( |
17 | diff --git a/src/maasserver/api/machines.py b/src/maasserver/api/machines.py |
18 | index bb50d67..b9e7d9f 100644 |
19 | --- a/src/maasserver/api/machines.py |
20 | +++ b/src/maasserver/api/machines.py |
21 | @@ -1226,10 +1226,11 @@ class MachineHandler(NodeHandler, OwnerDataMixin, PowerMixin): |
22 | machine = self.model.objects.get_node_or_404( |
23 | system_id=system_id, user=request.user, |
24 | perm=NodePermission.admin) |
25 | - if machine.status != NODE_STATUS.READY: |
26 | + if machine.status not in { |
27 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING}: |
28 | raise NodeStateViolation( |
29 | - "Machine must be in a ready state to restore networking " |
30 | - "configuration") |
31 | + "Machine must be in a ready or failed testing state to " |
32 | + "restore networking configuration") |
33 | machine.restore_network_interfaces() |
34 | machine.set_initial_networking_configuration() |
35 | return reload_object(machine) |
36 | @@ -1260,10 +1261,11 @@ class MachineHandler(NodeHandler, OwnerDataMixin, PowerMixin): |
37 | machine = self.model.objects.get_node_or_404( |
38 | system_id=system_id, user=request.user, |
39 | perm=NodePermission.admin) |
40 | - if machine.status != NODE_STATUS.READY: |
41 | + if machine.status not in { |
42 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING}: |
43 | raise NodeStateViolation( |
44 | - "Machine must be in a ready state to restore storage " |
45 | - "configuration.") |
46 | + "Machine must be in a ready or failed testing state to " |
47 | + "restore storage configuration.") |
48 | machine.set_default_storage_layout() |
49 | return reload_object(machine) |
50 | |
51 | @@ -1293,10 +1295,11 @@ class MachineHandler(NodeHandler, OwnerDataMixin, PowerMixin): |
52 | machine = self.model.objects.get_node_or_404( |
53 | system_id=system_id, user=request.user, |
54 | perm=NodePermission.admin) |
55 | - if machine.status != NODE_STATUS.READY: |
56 | + if machine.status not in { |
57 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING}: |
58 | raise NodeStateViolation( |
59 | - "Machine must be in a ready state to restore default " |
60 | - "networking and storage configuration.") |
61 | + "Machine must be in a ready or failed testing state to " |
62 | + "restore default networking and storage configuration.") |
63 | machine.set_default_storage_layout() |
64 | machine.restore_network_interfaces() |
65 | machine.set_initial_networking_configuration() |
66 | diff --git a/src/maasserver/api/tests/test_interfaces.py b/src/maasserver/api/tests/test_interfaces.py |
67 | index 3e40bd7..fc27dad 100644 |
68 | --- a/src/maasserver/api/tests/test_interfaces.py |
69 | +++ b/src/maasserver/api/tests/test_interfaces.py |
70 | @@ -1,4 +1,4 @@ |
71 | -# Copyright 2015-2017 Canonical Ltd. This software is licensed under the |
72 | +# Copyright 2015-2019 Canonical Ltd. This software is licensed under the |
73 | # GNU Affero General Public License version 3 (see the file LICENSE). |
74 | |
75 | """Tests for NodeInterfaces API.""" |
76 | @@ -14,6 +14,7 @@ from maasserver.enum import ( |
77 | INTERFACE_TYPE, |
78 | IPADDRESS_TYPE, |
79 | NODE_STATUS, |
80 | + NODE_STATUS_CHOICES, |
81 | NODE_TYPE, |
82 | ) |
83 | from maasserver.models import Interface |
84 | @@ -37,25 +38,14 @@ from testtools.matchers import ( |
85 | ) |
86 | |
87 | |
88 | -STATUSES = ( |
89 | - NODE_STATUS.NEW, |
90 | - NODE_STATUS.COMMISSIONING, |
91 | - NODE_STATUS.FAILED_COMMISSIONING, |
92 | - NODE_STATUS.MISSING, |
93 | - NODE_STATUS.RESERVED, |
94 | - NODE_STATUS.DEPLOYING, |
95 | - NODE_STATUS.DEPLOYED, |
96 | - NODE_STATUS.RETIRED, |
97 | - NODE_STATUS.FAILED_DEPLOYMENT, |
98 | - NODE_STATUS.RELEASING, |
99 | - NODE_STATUS.FAILED_RELEASING, |
100 | - NODE_STATUS.DISK_ERASING, |
101 | - NODE_STATUS.FAILED_DISK_ERASING, |
102 | - NODE_STATUS.ENTERING_RESCUE_MODE, |
103 | - NODE_STATUS.FAILED_ENTERING_RESCUE_MODE, |
104 | - NODE_STATUS.RESCUE_MODE, |
105 | - NODE_STATUS.EXITING_RESCUE_MODE, |
106 | - NODE_STATUS.FAILED_EXITING_RESCUE_MODE, |
107 | +EDITABLE_STATUSES = ( |
108 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING, |
109 | + NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN, |
110 | +) |
111 | + |
112 | +BLOCKED_STATUSES = ( |
113 | + status for status, _ in NODE_STATUS_CHOICES |
114 | + if status not in EDITABLE_STATUSES |
115 | ) |
116 | |
117 | |
118 | @@ -174,7 +164,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
119 | |
120 | def test_create_physical(self): |
121 | self.become_admin() |
122 | - for status in (NODE_STATUS.READY, NODE_STATUS.BROKEN): |
123 | + for status in EDITABLE_STATUSES: |
124 | node = factory.make_Node(status=status) |
125 | mac = factory.make_mac_address() |
126 | name = factory.make_name("eth") |
127 | @@ -240,8 +230,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
128 | |
129 | def test_create_physical_disabled(self): |
130 | self.become_admin() |
131 | - for status in ( |
132 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
133 | + for status in EDITABLE_STATUSES: |
134 | node = factory.make_Node(status=status) |
135 | mac = factory.make_mac_address() |
136 | name = factory.make_name("eth") |
137 | @@ -288,9 +277,9 @@ class TestInterfacesAPI(APITestCase.ForUser): |
138 | self.assertEqual( |
139 | http.client.FORBIDDEN, response.status_code, response.content) |
140 | |
141 | - def test_create_physical_409_when_not_ready_or_broken(self): |
142 | + def test_create_physical_409_when_invalid_status(self): |
143 | self.become_admin() |
144 | - for status in STATUSES: |
145 | + for status in BLOCKED_STATUSES: |
146 | node = factory.make_Node(status=status) |
147 | mac = factory.make_mac_address() |
148 | name = factory.make_name("eth") |
149 | @@ -307,7 +296,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
150 | |
151 | def test_create_physical_requires_mac(self): |
152 | self.become_admin() |
153 | - node = factory.make_Node(status=NODE_STATUS.READY) |
154 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
155 | uri = get_interfaces_uri(node) |
156 | response = self.client.post(uri, { |
157 | "op": "create_physical", |
158 | @@ -320,7 +309,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
159 | |
160 | def test_create_physical_doesnt_allow_mac_already_register(self): |
161 | self.become_admin() |
162 | - node = factory.make_Node(status=NODE_STATUS.READY) |
163 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
164 | interface_on_other_node = factory.make_Interface( |
165 | INTERFACE_TYPE.PHYSICAL) |
166 | name = factory.make_name("eth") |
167 | @@ -342,8 +331,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
168 | |
169 | def test_create_bond(self): |
170 | self.become_admin() |
171 | - for status in ( |
172 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
173 | + for status in EDITABLE_STATUSES: |
174 | node = factory.make_Node(status=status) |
175 | vlan = factory.make_VLAN() |
176 | parent_1_iface = factory.make_Interface( |
177 | @@ -413,9 +401,9 @@ class TestInterfacesAPI(APITestCase.ForUser): |
178 | self.assertEqual( |
179 | http.client.FORBIDDEN, response.status_code, response.content) |
180 | |
181 | - def test_create_bond_409_when_not_ready_or_broken(self): |
182 | + def test_create_bond_409_when_invalid_status(self): |
183 | self.become_admin() |
184 | - for status in STATUSES: |
185 | + for status in BLOCKED_STATUSES: |
186 | node = factory.make_Node(status=status) |
187 | vlan = factory.make_VLAN() |
188 | parent_1_iface = factory.make_Interface( |
189 | @@ -436,7 +424,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
190 | |
191 | def test_create_bond_requires_name_vlan_and_parents(self): |
192 | self.become_admin() |
193 | - node = factory.make_Node(status=NODE_STATUS.READY) |
194 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
195 | uri = get_interfaces_uri(node) |
196 | response = self.client.post(uri, { |
197 | "op": "create_bond", |
198 | @@ -452,7 +440,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
199 | |
200 | def test_create_vlan(self): |
201 | self.become_admin() |
202 | - node = factory.make_Node(status=NODE_STATUS.READY) |
203 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
204 | untagged_vlan = factory.make_VLAN() |
205 | parent_iface = factory.make_Interface( |
206 | INTERFACE_TYPE.PHYSICAL, vlan=untagged_vlan, node=node) |
207 | @@ -495,7 +483,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
208 | http.client.NOT_FOUND, response.status_code, response.content) |
209 | |
210 | def test_create_vlan_requires_admin(self): |
211 | - node = factory.make_Node(status=NODE_STATUS.READY) |
212 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
213 | untagged_vlan = factory.make_VLAN() |
214 | parent_iface = factory.make_Interface( |
215 | INTERFACE_TYPE.PHYSICAL, vlan=untagged_vlan, node=node) |
216 | @@ -511,7 +499,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
217 | |
218 | def test_create_vlan_requires_vlan_and_parent(self): |
219 | self.become_admin() |
220 | - node = factory.make_Node(status=NODE_STATUS.READY) |
221 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
222 | uri = get_interfaces_uri(node) |
223 | response = self.client.post(uri, { |
224 | "op": "create_vlan", |
225 | @@ -527,7 +515,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
226 | def test_create_bridge(self): |
227 | self.become_admin() |
228 | name = factory.make_name("br") |
229 | - node = factory.make_Node(status=NODE_STATUS.READY) |
230 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
231 | untagged_vlan = factory.make_VLAN() |
232 | parent_iface = factory.make_Interface( |
233 | INTERFACE_TYPE.PHYSICAL, vlan=untagged_vlan, node=node) |
234 | @@ -571,7 +559,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
235 | http.client.NOT_FOUND, response.status_code, response.content) |
236 | |
237 | def test_create_bridge_requires_admin(self): |
238 | - node = factory.make_Node(status=NODE_STATUS.READY) |
239 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
240 | untagged_vlan = factory.make_VLAN() |
241 | parent_iface = factory.make_Interface( |
242 | INTERFACE_TYPE.PHYSICAL, vlan=untagged_vlan, node=node) |
243 | @@ -586,7 +574,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
244 | |
245 | def test_create_bridge_requires_name_and_parent(self): |
246 | self.become_admin() |
247 | - node = factory.make_Node(status=NODE_STATUS.READY) |
248 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
249 | uri = get_interfaces_uri(node) |
250 | response = self.client.post(uri, { |
251 | "op": "create_bridge", |
252 | @@ -603,8 +591,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
253 | def test_create_acquired_bridge(self): |
254 | self.become_admin() |
255 | name = factory.make_name("br") |
256 | - node = factory.make_Node( |
257 | - status=NODE_STATUS.ALLOCATED, owner=self.user) |
258 | + node = factory.make_Node(status=NODE_STATUS.ALLOCATED, owner=self.user) |
259 | parent_fabric = factory.make_Fabric() |
260 | parent_vlan = parent_fabric.get_default_vlan() |
261 | parent_iface = factory.make_Interface( |
262 | @@ -651,7 +638,7 @@ class TestInterfacesAPI(APITestCase.ForUser): |
263 | |
264 | def test_create_acquired_bridge_not_allowed_in_ready(self): |
265 | name = factory.make_name("br") |
266 | - node = factory.make_Node(status=NODE_STATUS.READY) |
267 | + node = factory.make_Node(status=random.choice(EDITABLE_STATUSES)) |
268 | parent_fabric = factory.make_Fabric() |
269 | parent_vlan = parent_fabric.get_default_vlan() |
270 | parent_iface = factory.make_Interface( |
271 | @@ -933,8 +920,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
272 | |
273 | def test_update_physical_interface(self): |
274 | self.become_admin() |
275 | - for status in ( |
276 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
277 | + for status in EDITABLE_STATUSES: |
278 | node = factory.make_Node(status=status) |
279 | interface = factory.make_Interface( |
280 | INTERFACE_TYPE.PHYSICAL, node=node) |
281 | @@ -984,8 +970,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
282 | |
283 | def test_update_bond_interface(self): |
284 | self.become_admin() |
285 | - for status in ( |
286 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
287 | + for status in EDITABLE_STATUSES: |
288 | node = factory.make_Node(status=status) |
289 | bond, [nic_0, nic_1], [vlan_10, vlan_11] = make_complex_interface( |
290 | node) |
291 | @@ -1000,8 +985,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
292 | |
293 | def test_update_vlan_interface(self): |
294 | self.become_admin() |
295 | - for status in ( |
296 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
297 | + for status in EDITABLE_STATUSES: |
298 | node = factory.make_Node(status=status) |
299 | bond, [nic_0, nic_1], [vlan_10, vlan_11] = make_complex_interface( |
300 | node) |
301 | @@ -1030,11 +1014,11 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
302 | |
303 | def test_update_409_when_not_ready_broken_or_deployed(self): |
304 | self.become_admin() |
305 | - statuses = list(STATUSES) |
306 | - # Update is the only call that a deployed node can have called on |
307 | - # its interface. |
308 | - statuses.remove(NODE_STATUS.DEPLOYED) |
309 | - for status in statuses: |
310 | + for status in BLOCKED_STATUSES: |
311 | + # Update is the only call that a deployed node can have called on |
312 | + # its interface. |
313 | + if status == NODE_STATUS.DEPLOYED: |
314 | + continue |
315 | node = factory.make_Node(interface=True, status=status) |
316 | interface = factory.make_Interface( |
317 | INTERFACE_TYPE.PHYSICAL, node=node) |
318 | @@ -1048,8 +1032,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
319 | |
320 | def test_delete_deletes_interface(self): |
321 | self.become_admin() |
322 | - for status in ( |
323 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
324 | + for status in EDITABLE_STATUSES: |
325 | node = factory.make_Node(interface=True, status=status) |
326 | interface = node.get_boot_interface() |
327 | uri = get_interface_uri(interface) |
328 | @@ -1089,9 +1072,9 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
329 | self.assertEqual( |
330 | http.client.NOT_FOUND, response.status_code, response.content) |
331 | |
332 | - def test_delete_409_when_not_ready_or_broken(self): |
333 | + def test_delete_409_when_invalid_status(self): |
334 | self.become_admin() |
335 | - for status in STATUSES: |
336 | + for status in BLOCKED_STATUSES: |
337 | node = factory.make_Node(interface=True, status=status) |
338 | interface = node.get_boot_interface() |
339 | uri = get_interface_uri(interface) |
340 | @@ -1104,8 +1087,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
341 | # This just tests that the form is saved and the updated interface |
342 | # is returned. |
343 | self.become_admin() |
344 | - for status in ( |
345 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
346 | + for status in EDITABLE_STATUSES: |
347 | node = factory.make_Node(interface=True, status=status) |
348 | interface = node.get_boot_interface() |
349 | uri = get_interface_uri(interface) |
350 | @@ -1144,7 +1126,8 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
351 | |
352 | def test_link_subnet_allows_subnet_with_link_up(self): |
353 | self.become_admin() |
354 | - node = factory.make_Node(owner=self.user, status=NODE_STATUS.READY) |
355 | + node = factory.make_Node( |
356 | + owner=self.user, status=random.choice(EDITABLE_STATUSES)) |
357 | interface = factory.make_Interface( |
358 | INTERFACE_TYPE.PHYSICAL, node=node) |
359 | subnet = factory.make_Subnet(vlan=interface.vlan) |
360 | @@ -1166,7 +1149,8 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
361 | |
362 | def test_link_subnet_allows_link_up_subnet_to_be_cleared(self): |
363 | self.become_admin() |
364 | - node = factory.make_Node(owner=self.user, status=NODE_STATUS.READY) |
365 | + node = factory.make_Node( |
366 | + owner=self.user, status=random.choice(EDITABLE_STATUSES)) |
367 | interface = factory.make_Interface( |
368 | INTERFACE_TYPE.PHYSICAL, node=node) |
369 | subnet = factory.make_Subnet(vlan=interface.vlan) |
370 | @@ -1191,7 +1175,8 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
371 | |
372 | def test_link_subnet_allows_link_up_subnet_to_be_changed(self): |
373 | self.become_admin() |
374 | - node = factory.make_Node(owner=self.user, status=NODE_STATUS.READY) |
375 | + node = factory.make_Node( |
376 | + owner=self.user, status=random.choice(EDITABLE_STATUSES)) |
377 | interface = factory.make_Interface( |
378 | INTERFACE_TYPE.PHYSICAL, node=node) |
379 | subnet = factory.make_Subnet(vlan=interface.vlan) |
380 | @@ -1219,7 +1204,8 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
381 | |
382 | def test_link_subnet_disallows_subnets_on_another_vlan(self): |
383 | self.become_admin() |
384 | - node = factory.make_Node(owner=self.user, status=NODE_STATUS.READY) |
385 | + node = factory.make_Node( |
386 | + owner=self.user, status=random.choice(EDITABLE_STATUSES)) |
387 | interface = factory.make_Interface( |
388 | INTERFACE_TYPE.PHYSICAL, node=node) |
389 | vlan2 = factory.make_VLAN() |
390 | @@ -1241,7 +1227,8 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
391 | |
392 | def test_link_subnet_allows_link_up_subnet_to_be_forcibly_changed(self): |
393 | self.become_admin() |
394 | - node = factory.make_Node(owner=self.user, status=NODE_STATUS.READY) |
395 | + node = factory.make_Node( |
396 | + owner=self.user, status=random.choice(EDITABLE_STATUSES)) |
397 | interface = factory.make_Interface( |
398 | INTERFACE_TYPE.PHYSICAL, node=node) |
399 | vlan2 = factory.make_VLAN() |
400 | @@ -1271,7 +1258,8 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
401 | |
402 | def test_link_subnet_force_link_up_deletes_existing_links(self): |
403 | self.become_admin() |
404 | - node = factory.make_Node(owner=self.user, status=NODE_STATUS.READY) |
405 | + node = factory.make_Node( |
406 | + owner=self.user, status=random.choice(EDITABLE_STATUSES)) |
407 | interface = factory.make_Interface( |
408 | INTERFACE_TYPE.PHYSICAL, node=node) |
409 | vlan2 = factory.make_VLAN() |
410 | @@ -1301,7 +1289,8 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
411 | |
412 | def test_link_subnet_without_force_link_up_returns_bad_request(self): |
413 | self.become_admin() |
414 | - node = factory.make_Node(owner=self.user, status=NODE_STATUS.READY) |
415 | + node = factory.make_Node( |
416 | + owner=self.user, status=random.choice(EDITABLE_STATUSES)) |
417 | interface = factory.make_Interface( |
418 | INTERFACE_TYPE.PHYSICAL, node=node) |
419 | vlan2 = factory.make_VLAN() |
420 | @@ -1343,8 +1332,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
421 | |
422 | def test_link_subnet_raises_error(self): |
423 | self.become_admin() |
424 | - for status in ( |
425 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
426 | + for status in EDITABLE_STATUSES: |
427 | node = factory.make_Node(interface=True, status=status) |
428 | interface = node.get_boot_interface() |
429 | uri = get_interface_uri(interface) |
430 | @@ -1368,9 +1356,9 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
431 | self.assertEqual( |
432 | http.client.FORBIDDEN, response.status_code, response.content) |
433 | |
434 | - def test_link_subnet_409_when_not_ready_or_broken(self): |
435 | + def test_link_subnet_409_when_invalid_status(self): |
436 | self.become_admin() |
437 | - for status in STATUSES: |
438 | + for status in BLOCKED_STATUSES: |
439 | node = factory.make_Node(interface=True, status=status) |
440 | interface = node.get_boot_interface() |
441 | uri = get_interface_uri(interface) |
442 | @@ -1386,8 +1374,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
443 | # This just tests that the form is saved and the updated interface |
444 | # is returned. |
445 | self.become_admin() |
446 | - for status in ( |
447 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
448 | + for status in EDITABLE_STATUSES: |
449 | node = factory.make_Node(interface=True, status=status) |
450 | interface = node.get_boot_interface() |
451 | subnet = factory.make_Subnet() |
452 | @@ -1424,8 +1411,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
453 | |
454 | def test_unlink_subnet_raises_error(self): |
455 | self.become_admin() |
456 | - for status in ( |
457 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
458 | + for status in EDITABLE_STATUSES: |
459 | node = factory.make_Node(interface=True, status=status) |
460 | interface = node.get_boot_interface() |
461 | uri = get_interface_uri(interface) |
462 | @@ -1449,9 +1435,9 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
463 | self.assertEqual( |
464 | http.client.FORBIDDEN, response.status_code, response.content) |
465 | |
466 | - def test_unlink_subnet_409_when_not_ready_or_broken(self): |
467 | + def test_unlink_subnet_409_when_invalid_status(self): |
468 | self.become_admin() |
469 | - for status in STATUSES: |
470 | + for status in BLOCKED_STATUSES: |
471 | node = factory.make_Node(interface=True, status=status) |
472 | interface = node.get_boot_interface() |
473 | uri = get_interface_uri(interface) |
474 | @@ -1466,8 +1452,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
475 | # This just tests that the form is saved and the updated interface |
476 | # is returned. |
477 | self.become_admin() |
478 | - for status in ( |
479 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
480 | + for status in EDITABLE_STATUSES: |
481 | node = factory.make_Node(interface=True, status=status) |
482 | interface = node.get_boot_interface() |
483 | subnet = factory.make_Subnet() |
484 | @@ -1493,9 +1478,9 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
485 | self.assertEqual( |
486 | http.client.FORBIDDEN, response.status_code, response.content) |
487 | |
488 | - def test_disconnect_409_when_not_ready_or_broken(self): |
489 | + def test_disconnect_409_when_invalid_status(self): |
490 | self.become_admin() |
491 | - for status in STATUSES: |
492 | + for status in BLOCKED_STATUSES: |
493 | node = factory.make_Node(interface=True, status=status) |
494 | interface = node.get_boot_interface() |
495 | uri = get_interface_uri(interface) |
496 | @@ -1509,8 +1494,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
497 | # The form that is used is fully tested in test_forms_interface_link. |
498 | # This just tests that the form is saved and the node link is created. |
499 | self.become_admin() |
500 | - for status in ( |
501 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
502 | + for status in EDITABLE_STATUSES: |
503 | node = factory.make_Node(interface=True, status=status) |
504 | interface = node.get_boot_interface() |
505 | network = factory.make_ipv4_network() |
506 | @@ -1532,8 +1516,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
507 | # The form that is used is fully tested in test_forms_interface_link. |
508 | # This just tests that the form is saved and the node link is created. |
509 | self.become_admin() |
510 | - for status in ( |
511 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
512 | + for status in EDITABLE_STATUSES: |
513 | node = factory.make_Node(interface=True, status=status) |
514 | interface = node.get_boot_interface() |
515 | network = factory.make_ipv6_network() |
516 | @@ -1553,8 +1536,7 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
517 | |
518 | def test_set_default_gateway_raises_error(self): |
519 | self.become_admin() |
520 | - for status in ( |
521 | - NODE_STATUS.READY, NODE_STATUS.ALLOCATED, NODE_STATUS.BROKEN): |
522 | + for status in EDITABLE_STATUSES: |
523 | node = factory.make_Node(interface=True, status=status) |
524 | interface = node.get_boot_interface() |
525 | uri = get_interface_uri(interface) |
526 | @@ -1578,9 +1560,9 @@ class TestNodeInterfaceAPI(APITransactionTestCase.ForUser): |
527 | self.assertEqual( |
528 | http.client.FORBIDDEN, response.status_code, response.content) |
529 | |
530 | - def test_set_default_gateway_409_when_not_ready_or_broken(self): |
531 | + def test_set_default_gateway_409_when_invalid_status(self): |
532 | self.become_admin() |
533 | - for status in STATUSES: |
534 | + for status in BLOCKED_STATUSES: |
535 | node = factory.make_Node(interface=True, status=status) |
536 | interface = node.get_boot_interface() |
537 | uri = get_interface_uri(interface) |
538 | diff --git a/src/maasserver/api/tests/test_machine.py b/src/maasserver/api/tests/test_machine.py |
539 | index 196b8b2..cc2c8a6 100644 |
540 | --- a/src/maasserver/api/tests/test_machine.py |
541 | +++ b/src/maasserver/api/tests/test_machine.py |
542 | @@ -2675,7 +2675,8 @@ class TestRestoreNetworkingConfiguration(APITestCase.ForUser): |
543 | |
544 | def test_restore_networking_configuration(self): |
545 | self.become_admin() |
546 | - machine = factory.make_Machine(status=NODE_STATUS.READY) |
547 | + machine = factory.make_Machine(status=choice([ |
548 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING])) |
549 | mock_restore_network_interfaces = self.patch( |
550 | node_module.Machine, 'restore_network_interfaces') |
551 | mock_set_initial_networking_config = self.patch( |
552 | @@ -2699,7 +2700,9 @@ class TestRestoreNetworkingConfiguration(APITestCase.ForUser): |
553 | |
554 | def test_restore_networking_configuration_checks_machine_status(self): |
555 | self.become_admin() |
556 | - machine = factory.make_Machine(status=NODE_STATUS.DEPLOYED) |
557 | + machine = factory.make_Machine(status=factory.pick_choice( |
558 | + NODE_STATUS_CHOICES, |
559 | + but_not=[NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING])) |
560 | mock_set_initial_networking_config = self.patch( |
561 | node_module.Machine, 'set_initial_networking_configuration') |
562 | response = self.client.post( |
563 | @@ -2719,7 +2722,8 @@ class TestRestoreStorageConfiguration(APITestCase.ForUser): |
564 | |
565 | def test_restore_storage_configuration(self): |
566 | self.become_admin() |
567 | - machine = factory.make_Machine(status=NODE_STATUS.READY) |
568 | + machine = factory.make_Machine(status=choice([ |
569 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING])) |
570 | mock_set_default_storage_layout = self.patch( |
571 | node_module.Machine, 'set_default_storage_layout') |
572 | response = self.client.post( |
573 | @@ -2740,7 +2744,9 @@ class TestRestoreStorageConfiguration(APITestCase.ForUser): |
574 | |
575 | def test_restore_storage_configuration_checks_machine_status(self): |
576 | self.become_admin() |
577 | - machine = factory.make_Machine(status=NODE_STATUS.DEPLOYED) |
578 | + machine = factory.make_Machine(status=factory.pick_choice( |
579 | + NODE_STATUS_CHOICES, |
580 | + but_not=[NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING])) |
581 | mock_set_default_storage_layout = self.patch( |
582 | node_module.Machine, 'set_default_storage_layout') |
583 | response = self.client.post( |
584 | @@ -2760,7 +2766,8 @@ class TestRestoreDefaultConfiguration(APITestCase.ForUser): |
585 | |
586 | def test_restore_default_configuration(self): |
587 | self.become_admin() |
588 | - machine = factory.make_Machine(status=NODE_STATUS.READY) |
589 | + machine = factory.make_Machine(status=choice([ |
590 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING])) |
591 | mock_set_default_storage_layout = self.patch( |
592 | node_module.Machine, 'set_default_storage_layout') |
593 | mock_restore_network_interfaces = self.patch( |
594 | @@ -2787,7 +2794,9 @@ class TestRestoreDefaultConfiguration(APITestCase.ForUser): |
595 | |
596 | def test_restore_default_configuration_checks_machine_status(self): |
597 | self.become_admin() |
598 | - machine = factory.make_Machine(status=NODE_STATUS.DEPLOYED) |
599 | + machine = factory.make_Machine(status=factory.pick_choice( |
600 | + NODE_STATUS_CHOICES, |
601 | + but_not=[NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING])) |
602 | mock_restore_default_configuration = self.patch( |
603 | node_module.Machine, 'restore_default_configuration') |
604 | response = self.client.post( |
605 | diff --git a/src/maasserver/forms/clone.py b/src/maasserver/forms/clone.py |
606 | index 6999193..5d59af3 100644 |
607 | --- a/src/maasserver/forms/clone.py |
608 | +++ b/src/maasserver/forms/clone.py |
609 | @@ -47,7 +47,8 @@ class CloneForm(forms.Form): |
610 | self.fields['destinations'].base_field.queryset = ( |
611 | Machine.objects.get_nodes( |
612 | self.user, NodePermission.admin, |
613 | - from_nodes=Machine.objects.filter(status=NODE_STATUS.READY))) |
614 | + from_nodes=Machine.objects.filter(status__in={ |
615 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING}))) |
616 | |
617 | def clean(self): |
618 | """Validate that the form is valid and that the destinations can accept |
619 | diff --git a/src/maasserver/forms/tests/test_clone.py b/src/maasserver/forms/tests/test_clone.py |
620 | index aa8738a..6db8e2d 100644 |
621 | --- a/src/maasserver/forms/tests/test_clone.py |
622 | +++ b/src/maasserver/forms/tests/test_clone.py |
623 | @@ -5,6 +5,8 @@ |
624 | |
625 | __all__ = [] |
626 | |
627 | +import random |
628 | + |
629 | from maasserver.enum import NODE_STATUS |
630 | from maasserver.forms.clone import CloneForm |
631 | from maasserver.testing.factory import factory |
632 | @@ -25,7 +27,8 @@ class TestCloneForm(MAASServerTestCase): |
633 | |
634 | def test__source_destination_match_error(self): |
635 | user = factory.make_admin() |
636 | - source = factory.make_Machine(status=NODE_STATUS.READY) |
637 | + source = factory.make_Machine(status=random.choice([ |
638 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING])) |
639 | form = CloneForm(user, data={ |
640 | 'source': source.system_id, |
641 | 'destinations': [source.system_id], |
642 | @@ -44,7 +47,9 @@ class TestCloneForm(MAASServerTestCase): |
643 | factory.make_PhysicalBlockDevice( |
644 | node=source, size=8 * 1024 ** 3, name="sda") |
645 | destination = factory.make_Machine( |
646 | - status=NODE_STATUS.READY, with_boot_disk=False) |
647 | + status=random.choice([ |
648 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING]), |
649 | + with_boot_disk=False) |
650 | factory.make_PhysicalBlockDevice( |
651 | node=destination, size=4 * 1024 ** 3, name="sda") |
652 | form = CloneForm(user, data={ |
653 | @@ -65,7 +70,9 @@ class TestCloneForm(MAASServerTestCase): |
654 | source = factory.make_Machine(with_boot_disk=False) |
655 | factory.make_Interface(node=source, name='eth0') |
656 | destination = factory.make_Machine( |
657 | - status=NODE_STATUS.READY, with_boot_disk=False) |
658 | + status=random.choice([ |
659 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING]), |
660 | + with_boot_disk=False) |
661 | factory.make_Interface(node=destination, name='eth1') |
662 | form = CloneForm(user, data={ |
663 | 'source': source.system_id, |
664 | @@ -87,7 +94,9 @@ class TestCloneForm(MAASServerTestCase): |
665 | node=source, size=8 * 1024 ** 3, name="sda") |
666 | factory.make_Interface(node=source, name='eth0') |
667 | destination = factory.make_Machine( |
668 | - status=NODE_STATUS.READY, with_boot_disk=False) |
669 | + status=random.choice([ |
670 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING]), |
671 | + with_boot_disk=False) |
672 | factory.make_PhysicalBlockDevice( |
673 | node=destination, size=8 * 1024 ** 3, name="sda") |
674 | factory.make_Interface(node=destination, name='eth0') |
675 | @@ -112,12 +121,16 @@ class TestCloneForm(MAASServerTestCase): |
676 | node=source, size=8 * 1024 ** 3, name="sda") |
677 | factory.make_Interface(node=source, name='eth0') |
678 | destination1 = factory.make_Machine( |
679 | - status=NODE_STATUS.READY, with_boot_disk=False) |
680 | + status=random.choice([ |
681 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING]), |
682 | + with_boot_disk=False) |
683 | factory.make_PhysicalBlockDevice( |
684 | node=destination1, size=8 * 1024 ** 3, name="sda") |
685 | factory.make_Interface(node=destination1, name='eth0') |
686 | destination2 = factory.make_Machine( |
687 | - status=NODE_STATUS.READY, with_boot_disk=False) |
688 | + status=random.choice([ |
689 | + NODE_STATUS.READY, NODE_STATUS.FAILED_TESTING]), |
690 | + with_boot_disk=False) |
691 | factory.make_PhysicalBlockDevice( |
692 | node=destination2, size=8 * 1024 ** 3, name="sda") |
693 | factory.make_Interface(node=destination2, name='eth0') |
694 | diff --git a/src/maasserver/static/js/angular/controllers/node_details_networking.js b/src/maasserver/static/js/angular/controllers/node_details_networking.js |
695 | index d0a80ec..4fda858 100644 |
696 | --- a/src/maasserver/static/js/angular/controllers/node_details_networking.js |
697 | +++ b/src/maasserver/static/js/angular/controllers/node_details_networking.js |
698 | @@ -727,7 +727,9 @@ export function NodeNetworkingController( |
699 | } |
700 | if ( |
701 | angular.isObject($scope.node) && |
702 | - ["New", "Ready", "Allocated", "Broken"].indexOf($scope.node.status) === -1 |
703 | + ["New", "Ready", "Failed testing", "Allocated", "Broken"].indexOf( |
704 | + $scope.node.status |
705 | + ) === -1 |
706 | ) { |
707 | // If a non-controller node is not ready allocated, or broken, |
708 | // disable networking panel. |
709 | diff --git a/src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js b/src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js |
710 | index c99f740..2764a15 100644 |
711 | --- a/src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js |
712 | +++ b/src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js |
713 | @@ -3412,6 +3412,8 @@ describe("NodeNetworkingController", function() { |
714 | expect($scope.isAllNetworkingDisabled()).toBe(false); |
715 | $scope.node = { status: "Ready" }; |
716 | expect($scope.isAllNetworkingDisabled()).toBe(false); |
717 | + $scope.node = { status: "Failed testing" }; |
718 | + expect($scope.isAllNetworkingDisabled()).toBe(false); |
719 | $scope.node = { status: "Allocated" }; |
720 | expect($scope.isAllNetworkingDisabled()).toBe(false); |
721 | $scope.node = { status: "Broken" }; |
UNIT TESTS network_ failed_ testing lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas
-b config_
STATUS: FAILED maas-ci- jenkins. internal: 8080/job/ maas/job/ branch- tester/ 6222/console 09783e0f96e6bc2 b9bea1dff0
LOG: http://
COMMIT: 3b28fb91bd6c0ee