Merge ~ack/maas:lock-unlock-machine-ui into maas:master

Proposed by Alberto Donato
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)
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

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/748/console
COMMIT: 4de6a9a9df5cabced15826cfc8b3aa22fa9de318

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/749/console
COMMIT: c92eae16a19f384621ec02a05eabaf0c5e99fd19

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/750/console
COMMIT: fbf1bf948bcda4e833b54f279c6777f8e3256b34

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/751/console
COMMIT: 557739c6ee76f11ff56fba230a893832bf53bb21

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 6d70369582f2dc90e2e4722b68c2c1388f819933

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/799/console
COMMIT: aae7b4c0a64671ff468ce07c23f890f24468f8cd

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 71c00214bc5cc8bbce29fe38cae9fb4aada0766d

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 52d5ce6cbf464368ee0fc6992d985d81072cba1d

review: Approve
Revision history for this message
Andres Rodriguez (andreserl) wrote :

lgtm!

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: FAILED BUILD
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/841/consoleText

Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: FAILED BUILD
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/843/consoleText

Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b lock-unlock-machine-ui lp:~ack/maas into -b master lp:~maas-committers/maas

STATUS: FAILED BUILD
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/865/consoleText

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/node_action.py b/src/maasserver/node_action.py
2index 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,
78diff --git a/src/maasserver/static/js/angular/controllers/node_details.js b/src/maasserver/static/js/angular/controllers/node_details.js
79index 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.
105diff --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
106index 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() {
128diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html
129index 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>
141diff --git a/src/maasserver/static/partials/nodes-list.html b/src/maasserver/static/partials/nodes-list.html
142index 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>
158diff --git a/src/maasserver/tests/test_node_action.py b/src/maasserver/tests/test_node_action.py
159index 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,
261diff --git a/src/maasserver/websockets/handlers/machine.py b/src/maasserver/websockets/handlers/machine.py
262index 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
584diff --git a/src/maasserver/websockets/handlers/tests/test_general.py b/src/maasserver/websockets/handlers/tests/test_general.py
585index 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):
598diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py
599index 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, {})

Subscribers

People subscribed via source and target branches