Merge ~ack/maas:lock-unlock-machine-ui into maas:master
- Git
- lp:~ack/maas
- lock-unlock-machine-ui
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Alberto Donato | ||||
Approved revision: | 52d5ce6cbf464368ee0fc6992d985d81072cba1d | ||||
Merge reported by: | MAAS Lander | ||||
Merged at revision: | not available | ||||
Proposed branch: | ~ack/maas:lock-unlock-machine-ui | ||||
Merge into: | maas:master | ||||
Prerequisite: | ~ack/maas:lock-unlock-machine-api | ||||
Diff against target: |
1112 lines (+518/-112) 9 files modified
src/maasserver/node_action.py (+43/-0) src/maasserver/static/js/angular/controllers/node_details.js (+11/-1) src/maasserver/static/js/angular/controllers/tests/test_node_details.js (+12/-0) src/maasserver/static/partials/node-details.html (+2/-0) src/maasserver/static/partials/nodes-list.html (+6/-0) src/maasserver/tests/test_node_action.py (+71/-0) src/maasserver/websockets/handlers/machine.py (+35/-108) src/maasserver/websockets/handlers/tests/test_general.py (+2/-1) src/maasserver/websockets/handlers/tests/test_machine.py (+336/-2) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andres Rodriguez (community) | Approve | ||
MAAS Lander | Approve | ||
Review via email: mp+333793@code.launchpad.net |
Commit message
LP: #1453878 - add lock/unlock actions in the UI
Description of the change
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b lock-unlock-
STATUS: FAILED
LOG: http://
COMMIT: c92eae16a19f384
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b lock-unlock-
STATUS: FAILED
LOG: http://
COMMIT: fbf1bf948bcda4e
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b lock-unlock-
STATUS: FAILED
LOG: http://
COMMIT: 557739c6ee76f11
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b lock-unlock-
STATUS: SUCCESS
COMMIT: 6d70369582f2dc9
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b lock-unlock-
STATUS: FAILED
LOG: http://
COMMIT: aae7b4c0a64671f
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b lock-unlock-
STATUS: SUCCESS
COMMIT: 71c00214bc5cc8b
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b lock-unlock-
STATUS: SUCCESS
COMMIT: 52d5ce6cbf46436
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b lock-unlock-
STATUS: FAILED BUILD
LOG: http://
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b lock-unlock-
STATUS: FAILED BUILD
LOG: http://
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b lock-unlock-
STATUS: FAILED BUILD
LOG: http://
There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.
Preview Diff
1 | diff --git a/src/maasserver/node_action.py b/src/maasserver/node_action.py |
2 | index 74c8df7..5b7435c 100644 |
3 | --- a/src/maasserver/node_action.py |
4 | +++ b/src/maasserver/node_action.py |
5 | @@ -108,6 +108,9 @@ class NodeAction(metaclass=ABCMeta): |
6 | # is being applied to a node_type which is node |
7 | node_permission = None |
8 | |
9 | + # Whether the action is allowed when the node is locked |
10 | + allowed_when_locked = False |
11 | + |
12 | def __init__(self, node, user, request=None): |
13 | """Initialize a node action. |
14 | |
15 | @@ -129,8 +132,11 @@ class NodeAction(metaclass=ABCMeta): |
16 | elif (self.node_permission == NODE_PERMISSION.ADMIN and |
17 | not self.user.is_superuser): |
18 | return False |
19 | + elif self.node.locked and not self.allowed_when_locked: |
20 | + return False |
21 | elif self.node.node_type == NODE_TYPE.MACHINE: |
22 | return self.node.status in self.actionable_statuses |
23 | + |
24 | return True |
25 | |
26 | def inhibit(self): |
27 | @@ -498,6 +504,41 @@ class MarkFixed(NodeAction): |
28 | return not script_failures.exists() |
29 | |
30 | |
31 | +class Lock(NodeAction): |
32 | + """Lock a node.""" |
33 | + |
34 | + name = "lock" |
35 | + display = "Lock" |
36 | + display_sentence = "Lock" |
37 | + actionable_statuses = (NODE_STATUS.DEPLOYED,) |
38 | + permission = NODE_PERMISSION.LOCK |
39 | + for_type = {NODE_TYPE.MACHINE} |
40 | + |
41 | + def execute(self): |
42 | + self.node.lock(self.user, "via web interface") |
43 | + |
44 | + |
45 | +class Unlock(NodeAction): |
46 | + """Unlock a node.""" |
47 | + |
48 | + name = "unlock" |
49 | + display = "Unlock" |
50 | + display_sentence = "Unlock" |
51 | + actionable_statuses = ALL_STATUSES |
52 | + permission = NODE_PERMISSION.LOCK |
53 | + for_type = {NODE_TYPE.MACHINE} |
54 | + allowed_when_locked = True |
55 | + |
56 | + def is_actionable(self): |
57 | + if not super().is_actionable(): |
58 | + return False |
59 | + # don't show action if not locked |
60 | + return self.node.locked |
61 | + |
62 | + def execute(self): |
63 | + self.node.unlock(self.user, "via web interface") |
64 | + |
65 | + |
66 | class OverrideFailedTesting(NodeAction): |
67 | """Override failed tests and reset node into a usable state.""" |
68 | name = "override-failed-testing" |
69 | @@ -601,6 +642,8 @@ ACTION_CLASSES = ( |
70 | MarkBroken, |
71 | MarkFixed, |
72 | OverrideFailedTesting, |
73 | + Lock, |
74 | + Unlock, |
75 | SetZone, |
76 | ImportImages, |
77 | Delete, |
78 | diff --git a/src/maasserver/static/js/angular/controllers/node_details.js b/src/maasserver/static/js/angular/controllers/node_details.js |
79 | index 1633a61..565dced 100644 |
80 | --- a/src/maasserver/static/js/angular/controllers/node_details.js |
81 | +++ b/src/maasserver/static/js/angular/controllers/node_details.js |
82 | @@ -739,11 +739,21 @@ angular.module('MAAS').controller('NodeDetailsController', [ |
83 | return $scope.power_types.length > 0; |
84 | }; |
85 | |
86 | + // Return true if the node is locked |
87 | + $scope.isLocked = function() { |
88 | + if ($scope.node === null) { |
89 | + return false; |
90 | + } |
91 | + |
92 | + return $scope.node.locked; |
93 | + }; |
94 | + |
95 | // Return true when the edit buttons can be clicked. |
96 | $scope.canEdit = function() { |
97 | return ( |
98 | $scope.isRackControllerConnected() && |
99 | - $scope.isSuperUser()); |
100 | + $scope.isSuperUser() && |
101 | + ! $scope.isLocked()); |
102 | }; |
103 | |
104 | // Called to edit the domain name. |
105 | diff --git a/src/maasserver/static/js/angular/controllers/tests/test_node_details.js b/src/maasserver/static/js/angular/controllers/tests/test_node_details.js |
106 | index 28f4b9b..cc583d4 100644 |
107 | --- a/src/maasserver/static/js/angular/controllers/tests/test_node_details.js |
108 | +++ b/src/maasserver/static/js/angular/controllers/tests/test_node_details.js |
109 | @@ -1439,6 +1439,18 @@ describe("NodeDetailsController", function() { |
110 | "isRackControllerConnected").and.returnValue(true); |
111 | expect($scope.canEdit()).toBe(true); |
112 | }); |
113 | + |
114 | + it("returns false if machine is locked", |
115 | + function() { |
116 | + var controller = makeController(); |
117 | + $scope.isController = false; |
118 | + spyOn( |
119 | + $scope, |
120 | + "isRackControllerConnected").and.returnValue(true); |
121 | + $scope.node = makeNode(); |
122 | + $scope.node.locked = true; |
123 | + expect($scope.canEdit()).toBe(false); |
124 | + }); |
125 | }); |
126 | |
127 | describe("editHeaderDomain", function() { |
128 | diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html |
129 | index bcbf37e..3741be9 100755 |
130 | --- a/src/maasserver/static/partials/node-details.html |
131 | +++ b/src/maasserver/static/partials/node-details.html |
132 | @@ -100,6 +100,8 @@ |
133 | <span data-ng-if="action.option.name === 'mark-broken'">Mark {$ type_name $}</span> |
134 | <span data-ng-if="action.option.name === 'mark-fixed'">Mark {$ type_name $}</span> |
135 | <span data-ng-if="action.option.name === 'override-failed-testing'">Override failed testing</span> |
136 | + <span data-ng-if="action.option.name === 'lock'">Lock {$ type_name $}</span> |
137 | + <span data-ng-if="action.option.name === 'unlock'">Unlock {$ type_name $}</span> |
138 | <span data-ng-if="action.option.name === 'delete'">Delete {$ type_name $}</span> |
139 | <span data-ng-if="action.option.name === 'import-images'">Import images</span> |
140 | </button> |
141 | diff --git a/src/maasserver/static/partials/nodes-list.html b/src/maasserver/static/partials/nodes-list.html |
142 | index 05cce9b..a817cf3 100644 |
143 | --- a/src/maasserver/static/partials/nodes-list.html |
144 | +++ b/src/maasserver/static/partials/nodes-list.html |
145 | @@ -159,6 +159,12 @@ |
146 | <span data-ng-if="tabs[tab].actionOption.name === 'override-failed-testing'">Override failed testing on {$ tabs[tab].selectedItems.length $} |
147 | <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
148 | </span> |
149 | + <span data-ng-if="tabs[tab].actionOption.name === 'lock'">Lock {$ tabs[tab].selectedItems.length $} |
150 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
151 | + </span> |
152 | + <span data-ng-if="tabs[tab].actionOption.name === 'unlock'">Unlock {$ tabs[tab].selectedItems.length $} |
153 | + <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
154 | + </span> |
155 | <span data-ng-if="tabs[tab].actionOption.name === 'delete'">Delete {$ tabs[tab].selectedItems.length $} |
156 | <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span> |
157 | </span> |
158 | diff --git a/src/maasserver/tests/test_node_action.py b/src/maasserver/tests/test_node_action.py |
159 | index a9368e0..9e0a3de 100644 |
160 | --- a/src/maasserver/tests/test_node_action.py |
161 | +++ b/src/maasserver/tests/test_node_action.py |
162 | @@ -39,6 +39,7 @@ from maasserver.node_action import ( |
163 | Deploy, |
164 | ExitRescueMode, |
165 | ImportImages, |
166 | + Lock, |
167 | MarkBroken, |
168 | MarkFixed, |
169 | NodeAction, |
170 | @@ -50,6 +51,7 @@ from maasserver.node_action import ( |
171 | RPC_EXCEPTIONS, |
172 | SetZone, |
173 | Test, |
174 | + Unlock, |
175 | ) |
176 | import maasserver.node_action as node_action_module |
177 | from maasserver.node_status import ( |
178 | @@ -270,6 +272,22 @@ class TestNodeAction(MAASServerTestCase): |
179 | node = factory.make_Node() |
180 | self.assertFalse(MyAction(node, factory.make_User()).is_actionable()) |
181 | |
182 | + def test_is_actionable_false_if_locked(self): |
183 | + |
184 | + class MyAction(FakeNodeAction): |
185 | + pass |
186 | + |
187 | + node = factory.make_Node(status=NODE_STATUS.DEPLOYED, locked=True) |
188 | + self.assertFalse(MyAction(node, factory.make_User()).is_actionable()) |
189 | + |
190 | + def test_is_actionable_true_if_allow_ed_when_locked(self): |
191 | + |
192 | + class MyAction(FakeNodeAction): |
193 | + allowed_when_locked = True |
194 | + |
195 | + node = factory.make_Node(status=NODE_STATUS.DEPLOYED, locked=True) |
196 | + self.assertTrue(MyAction(node, factory.make_User()).is_actionable()) |
197 | + |
198 | def test_delete_action_last_for_node(self): |
199 | node = factory.make_Node() |
200 | actions = compile_node_actions( |
201 | @@ -821,6 +839,59 @@ class TestPowerOffAction(MAASServerTestCase): |
202 | "Nodes already powered off can be powered off.") |
203 | |
204 | |
205 | +class TestLockAction(MAASServerTestCase): |
206 | + |
207 | + def test_changes_locked_status(self): |
208 | + user = factory.make_User() |
209 | + node = factory.make_Node(status=NODE_STATUS.DEPLOYED, owner=user) |
210 | + action = Lock(node, user) |
211 | + self.assertTrue(action.is_permitted()) |
212 | + action.execute() |
213 | + self.assertTrue(reload_object(node).locked) |
214 | + |
215 | + def test_not_actionable_if_not_deployed(self): |
216 | + user = factory.make_User() |
217 | + node = factory.make_Node(status=NODE_STATUS.READY, owner=user) |
218 | + action = Lock(node, user) |
219 | + self.assertFalse(action.is_actionable()) |
220 | + |
221 | + def test_not_actionable_if_locked(self): |
222 | + user = factory.make_User() |
223 | + node = factory.make_Node( |
224 | + status=NODE_STATUS.DEPLOYED, owner=user, locked=True) |
225 | + action = Lock(node, user) |
226 | + self.assertFalse(action.is_actionable()) |
227 | + |
228 | + def test_not_actionable_if_not_machine(self): |
229 | + user = factory.make_User() |
230 | + controller = factory.make_RackController() |
231 | + action = Lock(controller, user) |
232 | + self.assertFalse(action.is_actionable()) |
233 | + |
234 | + |
235 | +class TestUnlockAction(MAASServerTestCase): |
236 | + |
237 | + def test_changes_locked_status(self): |
238 | + user = factory.make_User() |
239 | + node = factory.make_Node(locked=True, owner=user) |
240 | + action = Unlock(node, user) |
241 | + self.assertTrue(action.is_permitted()) |
242 | + action.execute() |
243 | + self.assertFalse(reload_object(node).locked) |
244 | + |
245 | + def test_not_actionable_if_not_locked(self): |
246 | + user = factory.make_User() |
247 | + node = factory.make_Node(owner=user) |
248 | + action = Unlock(node, user) |
249 | + self.assertFalse(action.is_actionable()) |
250 | + |
251 | + def test_not_actionable_if_not_machine(self): |
252 | + user = factory.make_User() |
253 | + controller = factory.make_RackController() |
254 | + action = Unlock(controller, user) |
255 | + self.assertFalse(action.is_actionable()) |
256 | + |
257 | + |
258 | ACTIONABLE_STATUSES = [ |
259 | NODE_STATUS.DEPLOYING, |
260 | NODE_STATUS.FAILED_DEPLOYMENT, |
261 | diff --git a/src/maasserver/websockets/handlers/machine.py b/src/maasserver/websockets/handlers/machine.py |
262 | index b3126d3..67fe666 100644 |
263 | --- a/src/maasserver/websockets/handlers/machine.py |
264 | +++ b/src/maasserver/websockets/handlers/machine.py |
265 | @@ -350,10 +350,8 @@ class MachineHandler(NodeHandler): |
266 | |
267 | def update(self, params): |
268 | """Update the object from params.""" |
269 | - # Only admin users can perform update. |
270 | - if not reload_object(self.user).is_superuser: |
271 | - raise HandlerPermissionError() |
272 | - |
273 | + # check that the operation is allowed |
274 | + self._get_node_or_permission_error(params) |
275 | data = super(NodeHandler, self).update(params) |
276 | node_obj = Node.objects.get(system_id=data['system_id']) |
277 | |
278 | @@ -375,6 +373,8 @@ class MachineHandler(NodeHandler): |
279 | `m.api.machines`. |
280 | """ |
281 | machine = self.get_object(params) |
282 | + if machine.locked: |
283 | + raise HandlerPermissionError() |
284 | self._preflight_special_filesystem_modifications("mount", machine) |
285 | form = MountNonStorageFilesystemForm(machine, data=params) |
286 | if form.is_valid(): |
287 | @@ -391,6 +391,8 @@ class MachineHandler(NodeHandler): |
288 | `m.api.machines`. |
289 | """ |
290 | machine = self.get_object(params) |
291 | + if machine.locked: |
292 | + raise HandlerPermissionError() |
293 | self._preflight_special_filesystem_modifications("unmount", machine) |
294 | form = UnmountNonStorageFilesystemForm(machine, data=params) |
295 | if form.is_valid(): |
296 | @@ -414,6 +416,8 @@ class MachineHandler(NodeHandler): |
297 | |
298 | def update_filesystem(self, params): |
299 | node = self.get_object(params) |
300 | + if node.locked: |
301 | + raise HandlerPermissionError() |
302 | block_id = params.get('block_id') |
303 | partition_id = params.get('partition_id') |
304 | fstype = params.get('fstype') |
305 | @@ -523,11 +527,7 @@ class MachineHandler(NodeHandler): |
306 | |
307 | def update_disk(self, params): |
308 | """Update disk information.""" |
309 | - # Only admin users can perform delete. |
310 | - if not reload_object(self.user).is_superuser: |
311 | - raise HandlerPermissionError() |
312 | - |
313 | - node = self.get_object(params) |
314 | + node = self._get_node_or_permission_error(params) |
315 | device = BlockDevice.objects.get( |
316 | id=params['block_id'], node=node).actual_instance |
317 | if device.type == 'physical': |
318 | @@ -551,22 +551,14 @@ class MachineHandler(NodeHandler): |
319 | params.get('mount_options', '')) |
320 | |
321 | def delete_disk(self, params): |
322 | - # Only admin users can perform delete. |
323 | - if not reload_object(self.user).is_superuser: |
324 | - raise HandlerPermissionError() |
325 | - |
326 | - node = self.get_object(params) |
327 | + node = self._get_node_or_permission_error(params) |
328 | block_id = params.get('block_id') |
329 | if block_id is not None: |
330 | block_device = BlockDevice.objects.get(id=block_id, node=node) |
331 | block_device.delete() |
332 | |
333 | def delete_partition(self, params): |
334 | - # Only admin users can perform delete. |
335 | - if not reload_object(self.user).is_superuser: |
336 | - raise HandlerPermissionError() |
337 | - |
338 | - node = self.get_object(params) |
339 | + node = self._get_node_or_permission_error(params) |
340 | partition_id = params.get('partition_id') |
341 | if partition_id is not None: |
342 | partition = Partition.objects.get( |
343 | @@ -574,11 +566,7 @@ class MachineHandler(NodeHandler): |
344 | partition.delete() |
345 | |
346 | def delete_volume_group(self, params): |
347 | - # Only admin users can perform delete. |
348 | - if not reload_object(self.user).is_superuser: |
349 | - raise HandlerPermissionError() |
350 | - |
351 | - node = self.get_object(params) |
352 | + node = self._get_node_or_permission_error(params) |
353 | volume_group_id = params.get('volume_group_id') |
354 | if volume_group_id is not None: |
355 | volume_group = VolumeGroup.objects.get(id=volume_group_id) |
356 | @@ -587,11 +575,7 @@ class MachineHandler(NodeHandler): |
357 | volume_group.delete() |
358 | |
359 | def delete_cache_set(self, params): |
360 | - # Only admin users can perform delete. |
361 | - if not reload_object(self.user).is_superuser: |
362 | - raise HandlerPermissionError() |
363 | - |
364 | - node = self.get_object(params) |
365 | + node = self._get_node_or_permission_error(params) |
366 | cache_set_id = params.get('cache_set_id') |
367 | if cache_set_id is not None: |
368 | cache_set = CacheSet.objects.get(id=cache_set_id) |
369 | @@ -600,10 +584,7 @@ class MachineHandler(NodeHandler): |
370 | cache_set.delete() |
371 | |
372 | def delete_filesystem(self, params): |
373 | - # Only admin users can perform delete. |
374 | - if not reload_object(self.user).is_superuser: |
375 | - raise HandlerPermissionError() |
376 | - node = self.get_object(params) |
377 | + node = self._get_node_or_permission_error(params) |
378 | blockdevice_id = params.get('blockdevice_id') |
379 | partition_id = params.get('partition_id') |
380 | filesystem_id = params.get('filesystem_id') |
381 | @@ -618,11 +599,7 @@ class MachineHandler(NodeHandler): |
382 | |
383 | def create_partition(self, params): |
384 | """Create a partition.""" |
385 | - # Only admin users can perform delete. |
386 | - if not reload_object(self.user).is_superuser: |
387 | - raise HandlerPermissionError() |
388 | - |
389 | - node = self.get_object(params) |
390 | + node = self._get_node_or_permission_error(params) |
391 | disk_obj = BlockDevice.objects.get(id=params['block_id'], node=node) |
392 | form = AddPartitionForm( |
393 | disk_obj, { |
394 | @@ -640,11 +617,7 @@ class MachineHandler(NodeHandler): |
395 | |
396 | def create_cache_set(self, params): |
397 | """Create a cache set.""" |
398 | - # Only admin users can perform delete. |
399 | - if not reload_object(self.user).is_superuser: |
400 | - raise HandlerPermissionError() |
401 | - |
402 | - node = self.get_object(params) |
403 | + node = self._get_node_or_permission_error(params) |
404 | block_id = params.get('block_id') |
405 | partition_id = params.get('partition_id') |
406 | |
407 | @@ -665,11 +638,7 @@ class MachineHandler(NodeHandler): |
408 | |
409 | def create_bcache(self, params): |
410 | """Create a bcache.""" |
411 | - # Only admin users can perform delete. |
412 | - if not reload_object(self.user).is_superuser: |
413 | - raise HandlerPermissionError() |
414 | - |
415 | - node = self.get_object(params) |
416 | + node = self._get_node_or_permission_error(params) |
417 | block_id = params.get('block_id') |
418 | partition_id = params.get('partition_id') |
419 | |
420 | @@ -701,11 +670,7 @@ class MachineHandler(NodeHandler): |
421 | |
422 | def create_raid(self, params): |
423 | """Create a RAID.""" |
424 | - # Only admin users can perform delete. |
425 | - if not reload_object(self.user).is_superuser: |
426 | - raise HandlerPermissionError() |
427 | - |
428 | - node = self.get_object(params) |
429 | + node = self._get_node_or_permission_error(params) |
430 | form = CreateRaidForm(node=node, data=params) |
431 | if not form.is_valid(): |
432 | raise HandlerError(form.errors) |
433 | @@ -720,11 +685,7 @@ class MachineHandler(NodeHandler): |
434 | |
435 | def create_volume_group(self, params): |
436 | """Create a volume group.""" |
437 | - # Only admin users can perform delete. |
438 | - if not reload_object(self.user).is_superuser: |
439 | - raise HandlerPermissionError() |
440 | - |
441 | - node = self.get_object(params) |
442 | + node = self._get_node_or_permission_error(params) |
443 | form = CreateVolumeGroupForm(node=node, data=params) |
444 | if not form.is_valid(): |
445 | raise HandlerError(form.errors) |
446 | @@ -733,11 +694,7 @@ class MachineHandler(NodeHandler): |
447 | |
448 | def create_logical_volume(self, params): |
449 | """Create a logical volume.""" |
450 | - # Only admin users can perform delete. |
451 | - if not reload_object(self.user).is_superuser: |
452 | - raise HandlerPermissionError() |
453 | - |
454 | - node = self.get_object(params) |
455 | + node = self._get_node_or_permission_error(params) |
456 | volume_group = VolumeGroup.objects.get(id=params['volume_group_id']) |
457 | if volume_group.get_node() != node: |
458 | raise VolumeGroup.DoesNotExist() |
459 | @@ -759,11 +716,7 @@ class MachineHandler(NodeHandler): |
460 | |
461 | def set_boot_disk(self, params): |
462 | """Set the disk as the boot disk.""" |
463 | - # Only admin users can perform delete. |
464 | - if not reload_object(self.user).is_superuser: |
465 | - raise HandlerPermissionError() |
466 | - |
467 | - node = self.get_object(params) |
468 | + node = self._get_node_or_permission_error(params) |
469 | device = BlockDevice.objects.get( |
470 | id=params['block_id'], node=node).actual_instance |
471 | if device.type != 'physical': |
472 | @@ -803,11 +756,7 @@ class MachineHandler(NodeHandler): |
473 | |
474 | def create_physical(self, params): |
475 | """Create physical interface.""" |
476 | - # Only admin users can perform create. |
477 | - if not reload_object(self.user).is_superuser: |
478 | - raise HandlerPermissionError() |
479 | - |
480 | - node = self.get_object(params) |
481 | + node = self._get_node_or_permission_error(params) |
482 | form = PhysicalInterfaceForm(node=node, data=params) |
483 | if form.is_valid(): |
484 | interface = form.save() |
485 | @@ -818,11 +767,7 @@ class MachineHandler(NodeHandler): |
486 | |
487 | def create_vlan(self, params): |
488 | """Create VLAN interface.""" |
489 | - # Only admin users can perform create. |
490 | - if not reload_object(self.user).is_superuser: |
491 | - raise HandlerPermissionError() |
492 | - |
493 | - node = self.get_object(params) |
494 | + node = self._get_node_or_permission_error(params) |
495 | params['parents'] = [params.pop('parent')] |
496 | form = VLANInterfaceForm(node=node, data=params) |
497 | if form.is_valid(): |
498 | @@ -834,11 +779,7 @@ class MachineHandler(NodeHandler): |
499 | |
500 | def create_bond(self, params): |
501 | """Create bond interface.""" |
502 | - # Only admin users can perform create. |
503 | - if not reload_object(self.user).is_superuser: |
504 | - raise HandlerPermissionError() |
505 | - |
506 | - node = self.get_object(params) |
507 | + node = self._get_node_or_permission_error(params) |
508 | form = BondInterfaceForm(node=node, data=params) |
509 | if form.is_valid(): |
510 | interface = form.save() |
511 | @@ -849,11 +790,7 @@ class MachineHandler(NodeHandler): |
512 | |
513 | def create_bridge(self, params): |
514 | """Create bridge interface.""" |
515 | - # Only admin users can perform create. |
516 | - if not reload_object(self.user).is_superuser: |
517 | - raise HandlerPermissionError() |
518 | - |
519 | - node = self.get_object(params) |
520 | + node = self._get_node_or_permission_error(params) |
521 | if node.status == NODE_STATUS.ALLOCATED: |
522 | form = AcquiredBridgeInterfaceForm(node=node, data=params) |
523 | else: |
524 | @@ -867,11 +804,7 @@ class MachineHandler(NodeHandler): |
525 | |
526 | def update_interface(self, params): |
527 | """Update the interface.""" |
528 | - # Only admin users can perform update. |
529 | - if not reload_object(self.user).is_superuser: |
530 | - raise HandlerPermissionError() |
531 | - |
532 | - node = self.get_object(params) |
533 | + node = self._get_node_or_permission_error(params) |
534 | interface = Interface.objects.get(node=node, id=params["interface_id"]) |
535 | if node.status == NODE_STATUS.DEPLOYED: |
536 | interface_form = DeployedInterfaceForm |
537 | @@ -889,21 +822,13 @@ class MachineHandler(NodeHandler): |
538 | |
539 | def delete_interface(self, params): |
540 | """Delete the interface.""" |
541 | - # Only admin users can perform delete. |
542 | - if not reload_object(self.user).is_superuser: |
543 | - raise HandlerPermissionError() |
544 | - |
545 | - node = self.get_object(params) |
546 | + node = self._get_node_or_permission_error(params) |
547 | interface = Interface.objects.get(node=node, id=params["interface_id"]) |
548 | interface.delete() |
549 | |
550 | def link_subnet(self, params): |
551 | """Create or update the link.""" |
552 | - # Only admin users can perform update. |
553 | - if not reload_object(self.user).is_superuser: |
554 | - raise HandlerPermissionError() |
555 | - |
556 | - node = self.get_object(params) |
557 | + node = self._get_node_or_permission_error(params) |
558 | interface = Interface.objects.get(node=node, id=params["interface_id"]) |
559 | subnet = None |
560 | if "subnet" in params: |
561 | @@ -923,11 +848,7 @@ class MachineHandler(NodeHandler): |
562 | |
563 | def unlink_subnet(self, params): |
564 | """Delete the link.""" |
565 | - # Only admin users can perform unlink. |
566 | - if not reload_object(self.user).is_superuser: |
567 | - raise HandlerPermissionError() |
568 | - |
569 | - node = self.get_object(params) |
570 | + node = self._get_node_or_permission_error(params) |
571 | interface = Interface.objects.get(node=node, id=params["interface_id"]) |
572 | interface.unlink_subnet_by_id(params["link_id"]) |
573 | |
574 | @@ -958,3 +879,9 @@ class MachineHandler(NodeHandler): |
575 | d.addErrback(eb_error) |
576 | d.addCallback(partial(deferToDatabase, update_state)) |
577 | return d |
578 | + |
579 | + def _get_node_or_permission_error(self, params): |
580 | + node = self.get_object(params) |
581 | + if not reload_object(self.user).is_superuser or node.locked: |
582 | + raise HandlerPermissionError() |
583 | + return node |
584 | diff --git a/src/maasserver/websockets/handlers/tests/test_general.py b/src/maasserver/websockets/handlers/tests/test_general.py |
585 | index 8e4139b..f530de8 100644 |
586 | --- a/src/maasserver/websockets/handlers/tests/test_general.py |
587 | +++ b/src/maasserver/websockets/handlers/tests/test_general.py |
588 | @@ -164,7 +164,8 @@ class TestGeneralHandler(MAASServerTestCase): |
589 | self.assertItemsEqual( |
590 | ['release', 'mark-broken', 'on', 'deploy', 'mark-fixed', |
591 | 'commission', 'abort', 'acquire', 'off', 'rescue-mode', |
592 | - 'exit-rescue-mode', 'test', 'override-failed-testing'], |
593 | + 'exit-rescue-mode', 'lock', 'test', 'override-failed-testing', |
594 | + 'unlock'], |
595 | [action['name'] for action in handler.machine_actions({})]) |
596 | |
597 | def test_device_actions_for_admin(self): |
598 | diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py |
599 | index ea45b78..ceef773 100644 |
600 | --- a/src/maasserver/websockets/handlers/tests/test_machine.py |
601 | +++ b/src/maasserver/websockets/handlers/tests/test_machine.py |
602 | @@ -1799,10 +1799,19 @@ class TestMachineHandler(MAASServerTestCase): |
603 | |
604 | def test_update_raise_permissions_error_for_non_admin(self): |
605 | user = factory.make_User() |
606 | + node = factory.make_Node() |
607 | + handler = MachineHandler(user, {}) |
608 | + self.assertRaises( |
609 | + HandlerPermissionError, |
610 | + handler.update, {'system_id': node.system_id}) |
611 | + |
612 | + def test_update_raise_permissions_error_for_locked_node(self): |
613 | + user = factory.make_admin() |
614 | + node = factory.make_Node(locked=True) |
615 | handler = MachineHandler(user, {}) |
616 | self.assertRaises( |
617 | HandlerPermissionError, |
618 | - handler.update, {}) |
619 | + handler.update, {'system_id': node.system_id}) |
620 | |
621 | def test_update_raises_validation_error_for_invalid_architecture(self): |
622 | user = factory.make_admin() |
623 | @@ -1980,6 +1989,18 @@ class TestMachineHandler(MAASServerTestCase): |
624 | self.assertEqual(new_name, block_device.name) |
625 | self.assertItemsEqual(new_tags, block_device.tags) |
626 | |
627 | + def test_update_disk_locked_raises_permission_error(self): |
628 | + user = factory.make_admin() |
629 | + handler = MachineHandler(user, {}) |
630 | + node = factory.make_Node(locked=True) |
631 | + block_device = factory.make_PhysicalBlockDevice(node=node) |
632 | + new_name = factory.make_name("new") |
633 | + params = { |
634 | + 'system_id': node.system_id, |
635 | + 'block_id': block_device.id, |
636 | + 'name': new_name} |
637 | + self.assertRaises(HandlerPermissionError, handler.update_disk, params) |
638 | + |
639 | def test_delete_disk(self): |
640 | user = factory.make_admin() |
641 | handler = MachineHandler(user, {}) |
642 | @@ -1995,6 +2016,16 @@ class TestMachineHandler(MAASServerTestCase): |
643 | }) |
644 | self.assertIsNone(reload_object(block_device)) |
645 | |
646 | + def test_delete_disk_locked_raises_permission_error(self): |
647 | + user = factory.make_admin() |
648 | + handler = MachineHandler(user, {}) |
649 | + node = factory.make_Node(locked=True) |
650 | + block_device = factory.make_PhysicalBlockDevice(node=node) |
651 | + params = { |
652 | + 'system_id': node.system_id, |
653 | + 'block_id': block_device.id} |
654 | + self.assertRaises(HandlerPermissionError, handler.delete_disk, params) |
655 | + |
656 | def test_delete_partition(self): |
657 | user = factory.make_admin() |
658 | handler = MachineHandler(user, {}) |
659 | @@ -2010,6 +2041,17 @@ class TestMachineHandler(MAASServerTestCase): |
660 | }) |
661 | self.assertIsNone(reload_object(partition)) |
662 | |
663 | + def test_delete_partition_locked_raises_permission_error(self): |
664 | + user = factory.make_admin() |
665 | + handler = MachineHandler(user, {}) |
666 | + node = factory.make_Node(locked=True) |
667 | + partition = factory.make_Partition(node=node) |
668 | + params = { |
669 | + 'system_id': node.system_id, |
670 | + 'partition_id': partition.id} |
671 | + self.assertRaises( |
672 | + HandlerPermissionError, handler.delete_partition, params) |
673 | + |
674 | def test_delete_volume_group(self): |
675 | user = factory.make_admin() |
676 | handler = MachineHandler(user, {}) |
677 | @@ -2026,6 +2068,18 @@ class TestMachineHandler(MAASServerTestCase): |
678 | }) |
679 | self.assertIsNone(reload_object(volume_group)) |
680 | |
681 | + def test_delete_volume_group_locked_raises_permission_error(self): |
682 | + user = factory.make_admin() |
683 | + handler = MachineHandler(user, {}) |
684 | + node = factory.make_Node(locked=True) |
685 | + volume_group = factory.make_FilesystemGroup( |
686 | + node=node, group_type=FILESYSTEM_GROUP_TYPE.LVM_VG) |
687 | + params = { |
688 | + 'system_id': node.system_id, |
689 | + 'volume_group_id': volume_group.id} |
690 | + self.assertRaises( |
691 | + HandlerPermissionError, handler.delete_volume_group, params) |
692 | + |
693 | def test_delete_cache_set(self): |
694 | user = factory.make_admin() |
695 | handler = MachineHandler(user, {}) |
696 | @@ -2041,6 +2095,17 @@ class TestMachineHandler(MAASServerTestCase): |
697 | }) |
698 | self.assertIsNone(reload_object(cache_set)) |
699 | |
700 | + def test_delete_cache_set_locked_raises_permission_error(self): |
701 | + user = factory.make_admin() |
702 | + handler = MachineHandler(user, {}) |
703 | + node = factory.make_Node(locked=True) |
704 | + cache_set = factory.make_CacheSet(node=node) |
705 | + params = { |
706 | + 'system_id': node.system_id, |
707 | + 'cache_set_id': cache_set.id} |
708 | + self.assertRaises( |
709 | + HandlerPermissionError, handler.delete_cache_set, params) |
710 | + |
711 | def test_delete_filesystem_deletes_blockdevice_filesystem(self): |
712 | user = factory.make_admin() |
713 | handler = MachineHandler(user, {}) |
714 | @@ -2079,6 +2144,20 @@ class TestMachineHandler(MAASServerTestCase): |
715 | self.assertIsNone(reload_object(filesystem)) |
716 | self.assertIsNotNone(reload_object(partition)) |
717 | |
718 | + def test_delete_filesystem_locked_raises_permission_error(self): |
719 | + user = factory.make_admin() |
720 | + handler = MachineHandler(user, {}) |
721 | + node = factory.make_Node(locked=True) |
722 | + partition = factory.make_Partition(node=node) |
723 | + filesystem = factory.make_Filesystem( |
724 | + partition=partition, fstype=FILESYSTEM_TYPE.EXT4) |
725 | + params = { |
726 | + 'system_id': node.system_id, |
727 | + 'partition_id': partition.id, |
728 | + 'filesystem_id': filesystem.id} |
729 | + self.assertRaises( |
730 | + HandlerPermissionError, handler.delete_filesystem, params) |
731 | + |
732 | def test_create_partition(self): |
733 | user = factory.make_admin() |
734 | handler = MachineHandler(user, {}) |
735 | @@ -2142,6 +2221,29 @@ class TestMachineHandler(MAASServerTestCase): |
736 | fstype=fstype, mount_point=mount_point, |
737 | mount_options=mount_options)) |
738 | |
739 | + def test_create_partition_locked_raises_permission_error(self): |
740 | + user = factory.make_admin() |
741 | + handler = MachineHandler(user, {}) |
742 | + node = factory.make_Node(locked=True) |
743 | + block_device = factory.make_BlockDevice(node=node) |
744 | + partition_table = factory.make_PartitionTable( |
745 | + block_device=block_device, node=node) |
746 | + partition_table = factory.make_PartitionTable( |
747 | + block_device=block_device, node=node) |
748 | + size = partition_table.block_device.size // 2 |
749 | + fstype = factory.pick_filesystem_type() |
750 | + mount_point = factory.make_absolute_path() |
751 | + mount_options = factory.make_name("options") |
752 | + params = { |
753 | + 'system_id': node.system_id, |
754 | + 'block_id': partition_table.block_device_id, |
755 | + 'partition_size': size, |
756 | + 'fstype': fstype, |
757 | + 'mount_point': mount_point, |
758 | + 'mount_options': mount_options} |
759 | + self.assertRaises( |
760 | + HandlerPermissionError, handler.create_partition, params) |
761 | + |
762 | def test_create_cache_set_for_partition(self): |
763 | user = factory.make_admin() |
764 | handler = MachineHandler(user, {}) |
765 | @@ -2171,6 +2273,17 @@ class TestMachineHandler(MAASServerTestCase): |
766 | self.assertEqual( |
767 | block_device.id, cache_set.get_filesystem().block_device.id) |
768 | |
769 | + def test_create_cache_set_locked_raises_permission_error(self): |
770 | + user = factory.make_admin() |
771 | + handler = MachineHandler(user, {}) |
772 | + node = factory.make_Node(locked=True) |
773 | + partition = factory.make_Partition(node=node) |
774 | + params = { |
775 | + 'system_id': node.system_id, |
776 | + 'partition_id': partition.id} |
777 | + self.assertRaises( |
778 | + HandlerPermissionError, handler.create_cache_set, params) |
779 | + |
780 | def test_create_bcache_for_partition(self): |
781 | user = factory.make_admin() |
782 | handler = MachineHandler(user, {}) |
783 | @@ -2303,6 +2416,29 @@ class TestMachineHandler(MAASServerTestCase): |
784 | fstype=fstype, mount_point=mount_point, |
785 | mount_options=mount_options)) |
786 | |
787 | + def test_create_bcache_set_locked_raises_permission_error(self): |
788 | + user = factory.make_admin() |
789 | + handler = MachineHandler(user, {}) |
790 | + node = factory.make_Node(locked=True) |
791 | + block_device = factory.make_PhysicalBlockDevice(node=node) |
792 | + name = factory.make_name("bcache") |
793 | + cache_set = factory.make_CacheSet(node=node) |
794 | + cache_mode = factory.pick_enum(CACHE_MODE_TYPE) |
795 | + fstype = factory.pick_filesystem_type() |
796 | + mount_point = factory.make_absolute_path() |
797 | + mount_options = factory.make_name("options") |
798 | + params = { |
799 | + 'system_id': node.system_id, |
800 | + 'block_id': block_device.id, |
801 | + 'name': name, |
802 | + 'cache_set': cache_set.id, |
803 | + 'cache_mode': cache_mode, |
804 | + 'fstype': fstype, |
805 | + 'mount_point': mount_point, |
806 | + 'mount_options': mount_options} |
807 | + self.assertRaises( |
808 | + HandlerPermissionError, handler.create_bcache, params) |
809 | + |
810 | def test_create_raid(self): |
811 | user = factory.make_admin() |
812 | handler = MachineHandler(user, {}) |
813 | @@ -2363,6 +2499,20 @@ class TestMachineHandler(MAASServerTestCase): |
814 | fstype=fstype, mount_point=mount_point, |
815 | mount_options=mount_options)) |
816 | |
817 | + def test_create_raid_locked_raises_permission_error(self): |
818 | + user = factory.make_admin() |
819 | + handler = MachineHandler(user, {}) |
820 | + node = factory.make_Node(locked=True) |
821 | + disk0 = factory.make_PhysicalBlockDevice(node=node) |
822 | + disk1 = factory.make_PhysicalBlockDevice(node=node) |
823 | + params = { |
824 | + 'system_id': node.system_id, |
825 | + 'name': factory.make_name('md'), |
826 | + 'level': 'raid-1', |
827 | + 'block_devices': [disk0.id, disk1.id]} |
828 | + self.assertRaises( |
829 | + HandlerPermissionError, handler.create_raid, params) |
830 | + |
831 | def test_create_volume_group(self): |
832 | user = factory.make_admin() |
833 | handler = MachineHandler(user, {}) |
834 | @@ -2439,6 +2589,21 @@ class TestMachineHandler(MAASServerTestCase): |
835 | fstype=fstype, mount_point=mount_point, |
836 | mount_options=mount_options)) |
837 | |
838 | + def test_create_logical_volume_locked_raises_permission_error(self): |
839 | + user = factory.make_admin() |
840 | + handler = MachineHandler(user, {}) |
841 | + node = factory.make_Node(locked=True) |
842 | + volume_group = factory.make_FilesystemGroup( |
843 | + group_type=FILESYSTEM_GROUP_TYPE.LVM_VG, node=node) |
844 | + size = volume_group.get_lvm_free_space() |
845 | + params = { |
846 | + 'system_id': node.system_id, |
847 | + 'name': factory.make_name("lv"), |
848 | + 'volume_group_id': volume_group.id, |
849 | + 'size': size} |
850 | + self.assertRaises( |
851 | + HandlerPermissionError, handler.create_logical_volume, params) |
852 | + |
853 | def test_set_boot_disk(self): |
854 | user = factory.make_admin() |
855 | handler = MachineHandler(user, {}) |
856 | @@ -2464,6 +2629,17 @@ class TestMachineHandler(MAASServerTestCase): |
857 | self.assertEqual( |
858 | str(error), "Only a physical disk can be set as the boot disk.") |
859 | |
860 | + def test_set_boot_disk_locked_raises_permission_error(self): |
861 | + user = factory.make_admin() |
862 | + handler = MachineHandler(user, {}) |
863 | + node = factory.make_Node(locked=True) |
864 | + boot_disk = factory.make_PhysicalBlockDevice(node=node) |
865 | + params = { |
866 | + 'system_id': node.system_id, |
867 | + 'block_id': boot_disk.id} |
868 | + self.assertRaises( |
869 | + HandlerPermissionError, handler.set_boot_disk, params) |
870 | + |
871 | def test_update_raise_HandlerError_if_tag_has_definition(self): |
872 | user = factory.make_admin() |
873 | handler = MachineHandler(user, {}) |
874 | @@ -2625,6 +2801,19 @@ class TestMachineHandler(MAASServerTestCase): |
875 | node=node, type=INTERFACE_TYPE.VLAN, parents=interface)) |
876 | self.assertIsNotNone(vlan_interface) |
877 | |
878 | + def test_create_physical_locked_raises_permission_error(self): |
879 | + user = factory.make_admin() |
880 | + node = factory.make_Node(locked=True) |
881 | + handler = MachineHandler(user, {}) |
882 | + vlan = factory.make_VLAN() |
883 | + params = { |
884 | + "system_id": node.system_id, |
885 | + "name": factory.make_name("eth"), |
886 | + "mac_address": factory.make_mac_address(), |
887 | + "vlan": vlan.id} |
888 | + self.assertRaises( |
889 | + HandlerPermissionError, handler.create_physical, params) |
890 | + |
891 | def test_create_vlan_creates_link_auto(self): |
892 | user = factory.make_admin() |
893 | node = factory.make_Node() |
894 | @@ -2692,6 +2881,23 @@ class TestMachineHandler(MAASServerTestCase): |
895 | alloc_type=IPADDRESS_TYPE.STICKY, ip=None, subnet=new_subnet) |
896 | self.assertIsNotNone(link_up_ip) |
897 | |
898 | + def test_create_vlan_locked_raises_permission_error(self): |
899 | + user = factory.make_admin() |
900 | + node = factory.make_Node(locked=True) |
901 | + handler = MachineHandler(user, {}) |
902 | + vlan = factory.make_VLAN() |
903 | + interface = factory.make_Interface( |
904 | + INTERFACE_TYPE.PHYSICAL, node=node, vlan=vlan) |
905 | + new_subnet = factory.make_Subnet(vlan=vlan) |
906 | + params = { |
907 | + "system_id": node.system_id, |
908 | + "parent": interface.id, |
909 | + "vlan": vlan.id, |
910 | + "mode": INTERFACE_LINK_TYPE.AUTO, |
911 | + "subnet": new_subnet.id} |
912 | + self.assertRaises( |
913 | + HandlerPermissionError, handler.create_vlan, params) |
914 | + |
915 | def test_create_bond_creates_bond(self): |
916 | user = factory.make_admin() |
917 | node = factory.make_Node() |
918 | @@ -2729,6 +2935,24 @@ class TestMachineHandler(MAASServerTestCase): |
919 | "parents": [nic1.id, nic2.id], |
920 | }) |
921 | |
922 | + def test_create_bond_locked_raises_permission_error(self): |
923 | + user = factory.make_admin() |
924 | + node = factory.make_Node(locked=True) |
925 | + handler = MachineHandler(user, {}) |
926 | + nic1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
927 | + nic2 = factory.make_Interface( |
928 | + INTERFACE_TYPE.PHYSICAL, node=node, vlan=nic1.vlan) |
929 | + bond_mode = factory.pick_enum(BOND_MODE) |
930 | + params = { |
931 | + "system_id": node.system_id, |
932 | + "name": factory.make_name("bond"), |
933 | + "parents": [nic1.id, nic2.id], |
934 | + "mac_address": "%s" % nic1.mac_address, |
935 | + "vlan": nic1.vlan.id, |
936 | + "bond_mode": bond_mode} |
937 | + self.assertRaises( |
938 | + HandlerPermissionError, handler.create_bond, params) |
939 | + |
940 | def test_create_bridge_creates_bridge(self): |
941 | user = factory.make_admin() |
942 | node = factory.make_Node() |
943 | @@ -2765,6 +2989,22 @@ class TestMachineHandler(MAASServerTestCase): |
944 | "parents": [nic1.id], |
945 | }) |
946 | |
947 | + def test_create_bridge_locked_raises_permission_error(self): |
948 | + user = factory.make_admin() |
949 | + node = factory.make_Node(locked=True) |
950 | + handler = MachineHandler(user, {}) |
951 | + nic1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
952 | + params = { |
953 | + "system_id": node.system_id, |
954 | + "name": factory.make_name("br"), |
955 | + "parents": [nic1.id], |
956 | + "mac_address": "%s" % nic1.mac_address, |
957 | + "vlan": nic1.vlan.id, |
958 | + "bridge_stp": factory.pick_bool(), |
959 | + "bridge_fd": random.randint(0, 15)} |
960 | + self.assertRaises( |
961 | + HandlerPermissionError, handler.create_bridge, params) |
962 | + |
963 | def test_update_interface(self): |
964 | user = factory.make_admin() |
965 | node = factory.make_Node() |
966 | @@ -2814,6 +3054,20 @@ class TestMachineHandler(MAASServerTestCase): |
967 | "vlan": random.randint(1000, 5000), |
968 | }) |
969 | |
970 | + def test_update_interface_locked_raises_permission_error(self): |
971 | + user = factory.make_admin() |
972 | + node = factory.make_Node(locked=True) |
973 | + handler = MachineHandler(user, {}) |
974 | + interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
975 | + handler._script_results = {} |
976 | + handler._refresh_script_result_cache(node.get_latest_script_results) |
977 | + params = { |
978 | + "system_id": node.system_id, |
979 | + "interface_id": interface.id, |
980 | + "name": factory.make_name("name")} |
981 | + self.assertRaises( |
982 | + HandlerPermissionError, handler.update_interface, params) |
983 | + |
984 | def test_delete_interface(self): |
985 | user = factory.make_admin() |
986 | node = factory.make_Node() |
987 | @@ -2825,6 +3079,17 @@ class TestMachineHandler(MAASServerTestCase): |
988 | }) |
989 | self.assertIsNone(reload_object(interface)) |
990 | |
991 | + def test_delete_interface_locked_raises_permission_error(self): |
992 | + user = factory.make_admin() |
993 | + node = factory.make_Node(locked=True) |
994 | + handler = MachineHandler(user, {}) |
995 | + interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
996 | + params = { |
997 | + "system_id": node.system_id, |
998 | + "interface_id": interface.id} |
999 | + self.assertRaises( |
1000 | + HandlerPermissionError, handler.delete_interface, params) |
1001 | + |
1002 | def test_link_subnet_calls_update_link_by_id_if_link_id(self): |
1003 | user = factory.make_admin() |
1004 | node = factory.make_Node() |
1005 | @@ -2894,6 +3159,22 @@ class TestMachineHandler(MAASServerTestCase): |
1006 | MockCalledOnceWith( |
1007 | ANY, mode, subnet, ip_address=ip_address)) |
1008 | |
1009 | + def test_link_subnet_locked_raises_permission_error(self): |
1010 | + user = factory.make_admin() |
1011 | + node = factory.make_Node(locked=True) |
1012 | + handler = MachineHandler(user, {}) |
1013 | + interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
1014 | + subnet = factory.make_Subnet() |
1015 | + params = { |
1016 | + "system_id": node.system_id, |
1017 | + "interface_id": interface.id, |
1018 | + "link_id": factory.make_StaticIPAddress(interface=interface).id, |
1019 | + "subnet": subnet.id, |
1020 | + "mode": factory.pick_enum(INTERFACE_LINK_TYPE), |
1021 | + "ip_address": factory.make_ip_address()} |
1022 | + self.assertRaises( |
1023 | + HandlerPermissionError, handler.link_subnet, params) |
1024 | + |
1025 | def test_unlink_subnet(self): |
1026 | user = factory.make_admin() |
1027 | node = factory.make_Node() |
1028 | @@ -2901,13 +3182,27 @@ class TestMachineHandler(MAASServerTestCase): |
1029 | interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
1030 | link_ip = factory.make_StaticIPAddress( |
1031 | alloc_type=IPADDRESS_TYPE.AUTO, ip="", interface=interface) |
1032 | - handler.delete_interface({ |
1033 | + handler.unlink_subnet({ |
1034 | "system_id": node.system_id, |
1035 | "interface_id": interface.id, |
1036 | "link_id": link_ip.id, |
1037 | }) |
1038 | self.assertIsNone(reload_object(link_ip)) |
1039 | |
1040 | + def test_unlink_subnet_locked_raises_permission_error(self): |
1041 | + user = factory.make_admin() |
1042 | + node = factory.make_Node(locked=True) |
1043 | + handler = MachineHandler(user, {}) |
1044 | + interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
1045 | + link_ip = factory.make_StaticIPAddress( |
1046 | + alloc_type=IPADDRESS_TYPE.AUTO, ip="", interface=interface) |
1047 | + params = { |
1048 | + "system_id": node.system_id, |
1049 | + "interface_id": interface.id, |
1050 | + "link_id": link_ip.id} |
1051 | + self.assertRaises( |
1052 | + HandlerPermissionError, handler.unlink_subnet, params) |
1053 | + |
1054 | def test_get_grouped_storages_parses_blockdevices(self): |
1055 | user = factory.make_User() |
1056 | node = factory.make_Node(owner=user) |
1057 | @@ -3045,6 +3340,17 @@ class TestMachineHandlerMountSpecial(MAASServerTestCase): |
1058 | 'mount_point': Equals(["Enter a valid value."]), |
1059 | })) |
1060 | |
1061 | + def test_locked_raises_permission_error(self): |
1062 | + user = factory.make_admin() |
1063 | + handler = MachineHandler(user, {}) |
1064 | + machine = factory.make_Node(locked=True, owner=user) |
1065 | + params = { |
1066 | + 'system_id': machine.system_id, 'fstype': FILESYSTEM_TYPE.RAMFS, |
1067 | + 'mount_point': factory.make_absolute_path(), |
1068 | + } |
1069 | + self.assertRaises( |
1070 | + HandlerPermissionError, handler.mount_special, params) |
1071 | + |
1072 | |
1073 | class TestMachineHandlerMountSpecialScenarios(MAASServerTestCase): |
1074 | """Scenario tests for MachineHandler.mount_special.""" |
1075 | @@ -3247,9 +3553,37 @@ class TestMachineHandlerUnmountSpecialScenarios(MAASServerTestCase): |
1076 | raises_node_state_violation, |
1077 | "using status %d on %s" % (status, self.fstype)) |
1078 | |
1079 | + def test_locked_raises_permission_error(self): |
1080 | + admin = factory.make_admin() |
1081 | + node = factory.make_Node(locked=True, owner=admin) |
1082 | + filesystem = factory.make_Filesystem( |
1083 | + node=node, fstype=self.fstype, |
1084 | + mount_point=factory.make_absolute_path()) |
1085 | + handler = MachineHandler(admin, {}) |
1086 | + params = { |
1087 | + 'system_id': node.system_id, |
1088 | + 'mount_point': filesystem.mount_point} |
1089 | + self.assertRaises( |
1090 | + HandlerPermissionError, handler.unmount_special, params) |
1091 | + |
1092 | |
1093 | class TestMachineHandlerUpdateFilesystem(MAASServerTestCase): |
1094 | |
1095 | + def test_locked_raises_permission_error(self): |
1096 | + user = factory.make_admin() |
1097 | + handler = MachineHandler(user, {}) |
1098 | + node = factory.make_Node(locked=True) |
1099 | + block_device = factory.make_PhysicalBlockDevice(node=node) |
1100 | + fs = factory.make_Filesystem(block_device=block_device) |
1101 | + params = { |
1102 | + 'system_id': node.system_id, |
1103 | + 'block_id': block_device.id, |
1104 | + 'fstype': fs.fstype, |
1105 | + 'mount_point': None, |
1106 | + 'mount_options': None} |
1107 | + self.assertRaises( |
1108 | + HandlerPermissionError, handler.update_filesystem, params) |
1109 | + |
1110 | def test_unmount_blockdevice_filesystem(self): |
1111 | user = factory.make_admin() |
1112 | handler = MachineHandler(user, {}) |
UNIT TESTS machine- ui lp:~ack/maas into -b master lp:~maas-committers/maas
-b lock-unlock-
STATUS: FAILED maas-ci- jenkins. internal: 8080/job/ maas/job/ branch- tester/ 748/console ed15826cfc8b3aa 22fa9de318
LOG: http://
COMMIT: 4de6a9a9df5cabc