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
diff --git a/src/maasserver/node_action.py b/src/maasserver/node_action.py
index 74c8df7..5b7435c 100644
--- a/src/maasserver/node_action.py
+++ b/src/maasserver/node_action.py
@@ -108,6 +108,9 @@ class NodeAction(metaclass=ABCMeta):
108 # is being applied to a node_type which is node108 # is being applied to a node_type which is node
109 node_permission = None109 node_permission = None
110110
111 # Whether the action is allowed when the node is locked
112 allowed_when_locked = False
113
111 def __init__(self, node, user, request=None):114 def __init__(self, node, user, request=None):
112 """Initialize a node action.115 """Initialize a node action.
113116
@@ -129,8 +132,11 @@ class NodeAction(metaclass=ABCMeta):
129 elif (self.node_permission == NODE_PERMISSION.ADMIN and132 elif (self.node_permission == NODE_PERMISSION.ADMIN and
130 not self.user.is_superuser):133 not self.user.is_superuser):
131 return False134 return False
135 elif self.node.locked and not self.allowed_when_locked:
136 return False
132 elif self.node.node_type == NODE_TYPE.MACHINE:137 elif self.node.node_type == NODE_TYPE.MACHINE:
133 return self.node.status in self.actionable_statuses138 return self.node.status in self.actionable_statuses
139
134 return True140 return True
135141
136 def inhibit(self):142 def inhibit(self):
@@ -498,6 +504,41 @@ class MarkFixed(NodeAction):
498 return not script_failures.exists()504 return not script_failures.exists()
499505
500506
507class Lock(NodeAction):
508 """Lock a node."""
509
510 name = "lock"
511 display = "Lock"
512 display_sentence = "Lock"
513 actionable_statuses = (NODE_STATUS.DEPLOYED,)
514 permission = NODE_PERMISSION.LOCK
515 for_type = {NODE_TYPE.MACHINE}
516
517 def execute(self):
518 self.node.lock(self.user, "via web interface")
519
520
521class Unlock(NodeAction):
522 """Unlock a node."""
523
524 name = "unlock"
525 display = "Unlock"
526 display_sentence = "Unlock"
527 actionable_statuses = ALL_STATUSES
528 permission = NODE_PERMISSION.LOCK
529 for_type = {NODE_TYPE.MACHINE}
530 allowed_when_locked = True
531
532 def is_actionable(self):
533 if not super().is_actionable():
534 return False
535 # don't show action if not locked
536 return self.node.locked
537
538 def execute(self):
539 self.node.unlock(self.user, "via web interface")
540
541
501class OverrideFailedTesting(NodeAction):542class OverrideFailedTesting(NodeAction):
502 """Override failed tests and reset node into a usable state."""543 """Override failed tests and reset node into a usable state."""
503 name = "override-failed-testing"544 name = "override-failed-testing"
@@ -601,6 +642,8 @@ ACTION_CLASSES = (
601 MarkBroken,642 MarkBroken,
602 MarkFixed,643 MarkFixed,
603 OverrideFailedTesting,644 OverrideFailedTesting,
645 Lock,
646 Unlock,
604 SetZone,647 SetZone,
605 ImportImages,648 ImportImages,
606 Delete,649 Delete,
diff --git a/src/maasserver/static/js/angular/controllers/node_details.js b/src/maasserver/static/js/angular/controllers/node_details.js
index 1633a61..565dced 100644
--- a/src/maasserver/static/js/angular/controllers/node_details.js
+++ b/src/maasserver/static/js/angular/controllers/node_details.js
@@ -739,11 +739,21 @@ angular.module('MAAS').controller('NodeDetailsController', [
739 return $scope.power_types.length > 0;739 return $scope.power_types.length > 0;
740 };740 };
741741
742 // Return true if the node is locked
743 $scope.isLocked = function() {
744 if ($scope.node === null) {
745 return false;
746 }
747
748 return $scope.node.locked;
749 };
750
742 // Return true when the edit buttons can be clicked.751 // Return true when the edit buttons can be clicked.
743 $scope.canEdit = function() {752 $scope.canEdit = function() {
744 return (753 return (
745 $scope.isRackControllerConnected() &&754 $scope.isRackControllerConnected() &&
746 $scope.isSuperUser());755 $scope.isSuperUser() &&
756 ! $scope.isLocked());
747 };757 };
748758
749 // Called to edit the domain name.759 // Called to edit the domain name.
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
index 28f4b9b..cc583d4 100644
--- a/src/maasserver/static/js/angular/controllers/tests/test_node_details.js
+++ b/src/maasserver/static/js/angular/controllers/tests/test_node_details.js
@@ -1439,6 +1439,18 @@ describe("NodeDetailsController", function() {
1439 "isRackControllerConnected").and.returnValue(true);1439 "isRackControllerConnected").and.returnValue(true);
1440 expect($scope.canEdit()).toBe(true);1440 expect($scope.canEdit()).toBe(true);
1441 });1441 });
1442
1443 it("returns false if machine is locked",
1444 function() {
1445 var controller = makeController();
1446 $scope.isController = false;
1447 spyOn(
1448 $scope,
1449 "isRackControllerConnected").and.returnValue(true);
1450 $scope.node = makeNode();
1451 $scope.node.locked = true;
1452 expect($scope.canEdit()).toBe(false);
1453 });
1442 });1454 });
14431455
1444 describe("editHeaderDomain", function() {1456 describe("editHeaderDomain", function() {
diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html
index bcbf37e..3741be9 100755
--- a/src/maasserver/static/partials/node-details.html
+++ b/src/maasserver/static/partials/node-details.html
@@ -100,6 +100,8 @@
100 <span data-ng-if="action.option.name === 'mark-broken'">Mark {$ type_name $}</span>100 <span data-ng-if="action.option.name === 'mark-broken'">Mark {$ type_name $}</span>
101 <span data-ng-if="action.option.name === 'mark-fixed'">Mark {$ type_name $}</span>101 <span data-ng-if="action.option.name === 'mark-fixed'">Mark {$ type_name $}</span>
102 <span data-ng-if="action.option.name === 'override-failed-testing'">Override failed testing</span>102 <span data-ng-if="action.option.name === 'override-failed-testing'">Override failed testing</span>
103 <span data-ng-if="action.option.name === 'lock'">Lock {$ type_name $}</span>
104 <span data-ng-if="action.option.name === 'unlock'">Unlock {$ type_name $}</span>
103 <span data-ng-if="action.option.name === 'delete'">Delete {$ type_name $}</span>105 <span data-ng-if="action.option.name === 'delete'">Delete {$ type_name $}</span>
104 <span data-ng-if="action.option.name === 'import-images'">Import images</span>106 <span data-ng-if="action.option.name === 'import-images'">Import images</span>
105 </button>107 </button>
diff --git a/src/maasserver/static/partials/nodes-list.html b/src/maasserver/static/partials/nodes-list.html
index 05cce9b..a817cf3 100644
--- a/src/maasserver/static/partials/nodes-list.html
+++ b/src/maasserver/static/partials/nodes-list.html
@@ -159,6 +159,12 @@
159 <span data-ng-if="tabs[tab].actionOption.name === 'override-failed-testing'">Override failed testing on {$ tabs[tab].selectedItems.length $}159 <span data-ng-if="tabs[tab].actionOption.name === 'override-failed-testing'">Override failed testing on {$ tabs[tab].selectedItems.length $}
160 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>160 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
161 </span>161 </span>
162 <span data-ng-if="tabs[tab].actionOption.name === 'lock'">Lock {$ tabs[tab].selectedItems.length $}
163 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
164 </span>
165 <span data-ng-if="tabs[tab].actionOption.name === 'unlock'">Unlock {$ tabs[tab].selectedItems.length $}
166 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
167 </span>
162 <span data-ng-if="tabs[tab].actionOption.name === 'delete'">Delete {$ tabs[tab].selectedItems.length $}168 <span data-ng-if="tabs[tab].actionOption.name === 'delete'">Delete {$ tabs[tab].selectedItems.length $}
163 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>169 <span data-ng-pluralize count="tabs[tab].selectedItems.length" when="{'one': 'machine', 'other': 'machines'}"></span>
164 </span>170 </span>
diff --git a/src/maasserver/tests/test_node_action.py b/src/maasserver/tests/test_node_action.py
index a9368e0..9e0a3de 100644
--- a/src/maasserver/tests/test_node_action.py
+++ b/src/maasserver/tests/test_node_action.py
@@ -39,6 +39,7 @@ from maasserver.node_action import (
39 Deploy,39 Deploy,
40 ExitRescueMode,40 ExitRescueMode,
41 ImportImages,41 ImportImages,
42 Lock,
42 MarkBroken,43 MarkBroken,
43 MarkFixed,44 MarkFixed,
44 NodeAction,45 NodeAction,
@@ -50,6 +51,7 @@ from maasserver.node_action import (
50 RPC_EXCEPTIONS,51 RPC_EXCEPTIONS,
51 SetZone,52 SetZone,
52 Test,53 Test,
54 Unlock,
53)55)
54import maasserver.node_action as node_action_module56import maasserver.node_action as node_action_module
55from maasserver.node_status import (57from maasserver.node_status import (
@@ -270,6 +272,22 @@ class TestNodeAction(MAASServerTestCase):
270 node = factory.make_Node()272 node = factory.make_Node()
271 self.assertFalse(MyAction(node, factory.make_User()).is_actionable())273 self.assertFalse(MyAction(node, factory.make_User()).is_actionable())
272274
275 def test_is_actionable_false_if_locked(self):
276
277 class MyAction(FakeNodeAction):
278 pass
279
280 node = factory.make_Node(status=NODE_STATUS.DEPLOYED, locked=True)
281 self.assertFalse(MyAction(node, factory.make_User()).is_actionable())
282
283 def test_is_actionable_true_if_allow_ed_when_locked(self):
284
285 class MyAction(FakeNodeAction):
286 allowed_when_locked = True
287
288 node = factory.make_Node(status=NODE_STATUS.DEPLOYED, locked=True)
289 self.assertTrue(MyAction(node, factory.make_User()).is_actionable())
290
273 def test_delete_action_last_for_node(self):291 def test_delete_action_last_for_node(self):
274 node = factory.make_Node()292 node = factory.make_Node()
275 actions = compile_node_actions(293 actions = compile_node_actions(
@@ -821,6 +839,59 @@ class TestPowerOffAction(MAASServerTestCase):
821 "Nodes already powered off can be powered off.")839 "Nodes already powered off can be powered off.")
822840
823841
842class TestLockAction(MAASServerTestCase):
843
844 def test_changes_locked_status(self):
845 user = factory.make_User()
846 node = factory.make_Node(status=NODE_STATUS.DEPLOYED, owner=user)
847 action = Lock(node, user)
848 self.assertTrue(action.is_permitted())
849 action.execute()
850 self.assertTrue(reload_object(node).locked)
851
852 def test_not_actionable_if_not_deployed(self):
853 user = factory.make_User()
854 node = factory.make_Node(status=NODE_STATUS.READY, owner=user)
855 action = Lock(node, user)
856 self.assertFalse(action.is_actionable())
857
858 def test_not_actionable_if_locked(self):
859 user = factory.make_User()
860 node = factory.make_Node(
861 status=NODE_STATUS.DEPLOYED, owner=user, locked=True)
862 action = Lock(node, user)
863 self.assertFalse(action.is_actionable())
864
865 def test_not_actionable_if_not_machine(self):
866 user = factory.make_User()
867 controller = factory.make_RackController()
868 action = Lock(controller, user)
869 self.assertFalse(action.is_actionable())
870
871
872class TestUnlockAction(MAASServerTestCase):
873
874 def test_changes_locked_status(self):
875 user = factory.make_User()
876 node = factory.make_Node(locked=True, owner=user)
877 action = Unlock(node, user)
878 self.assertTrue(action.is_permitted())
879 action.execute()
880 self.assertFalse(reload_object(node).locked)
881
882 def test_not_actionable_if_not_locked(self):
883 user = factory.make_User()
884 node = factory.make_Node(owner=user)
885 action = Unlock(node, user)
886 self.assertFalse(action.is_actionable())
887
888 def test_not_actionable_if_not_machine(self):
889 user = factory.make_User()
890 controller = factory.make_RackController()
891 action = Unlock(controller, user)
892 self.assertFalse(action.is_actionable())
893
894
824ACTIONABLE_STATUSES = [895ACTIONABLE_STATUSES = [
825 NODE_STATUS.DEPLOYING,896 NODE_STATUS.DEPLOYING,
826 NODE_STATUS.FAILED_DEPLOYMENT,897 NODE_STATUS.FAILED_DEPLOYMENT,
diff --git a/src/maasserver/websockets/handlers/machine.py b/src/maasserver/websockets/handlers/machine.py
index b3126d3..67fe666 100644
--- a/src/maasserver/websockets/handlers/machine.py
+++ b/src/maasserver/websockets/handlers/machine.py
@@ -350,10 +350,8 @@ class MachineHandler(NodeHandler):
350350
351 def update(self, params):351 def update(self, params):
352 """Update the object from params."""352 """Update the object from params."""
353 # Only admin users can perform update.353 # check that the operation is allowed
354 if not reload_object(self.user).is_superuser:354 self._get_node_or_permission_error(params)
355 raise HandlerPermissionError()
356
357 data = super(NodeHandler, self).update(params)355 data = super(NodeHandler, self).update(params)
358 node_obj = Node.objects.get(system_id=data['system_id'])356 node_obj = Node.objects.get(system_id=data['system_id'])
359357
@@ -375,6 +373,8 @@ class MachineHandler(NodeHandler):
375 `m.api.machines`.373 `m.api.machines`.
376 """374 """
377 machine = self.get_object(params)375 machine = self.get_object(params)
376 if machine.locked:
377 raise HandlerPermissionError()
378 self._preflight_special_filesystem_modifications("mount", machine)378 self._preflight_special_filesystem_modifications("mount", machine)
379 form = MountNonStorageFilesystemForm(machine, data=params)379 form = MountNonStorageFilesystemForm(machine, data=params)
380 if form.is_valid():380 if form.is_valid():
@@ -391,6 +391,8 @@ class MachineHandler(NodeHandler):
391 `m.api.machines`.391 `m.api.machines`.
392 """392 """
393 machine = self.get_object(params)393 machine = self.get_object(params)
394 if machine.locked:
395 raise HandlerPermissionError()
394 self._preflight_special_filesystem_modifications("unmount", machine)396 self._preflight_special_filesystem_modifications("unmount", machine)
395 form = UnmountNonStorageFilesystemForm(machine, data=params)397 form = UnmountNonStorageFilesystemForm(machine, data=params)
396 if form.is_valid():398 if form.is_valid():
@@ -414,6 +416,8 @@ class MachineHandler(NodeHandler):
414416
415 def update_filesystem(self, params):417 def update_filesystem(self, params):
416 node = self.get_object(params)418 node = self.get_object(params)
419 if node.locked:
420 raise HandlerPermissionError()
417 block_id = params.get('block_id')421 block_id = params.get('block_id')
418 partition_id = params.get('partition_id')422 partition_id = params.get('partition_id')
419 fstype = params.get('fstype')423 fstype = params.get('fstype')
@@ -523,11 +527,7 @@ class MachineHandler(NodeHandler):
523527
524 def update_disk(self, params):528 def update_disk(self, params):
525 """Update disk information."""529 """Update disk information."""
526 # Only admin users can perform delete.530 node = self._get_node_or_permission_error(params)
527 if not reload_object(self.user).is_superuser:
528 raise HandlerPermissionError()
529
530 node = self.get_object(params)
531 device = BlockDevice.objects.get(531 device = BlockDevice.objects.get(
532 id=params['block_id'], node=node).actual_instance532 id=params['block_id'], node=node).actual_instance
533 if device.type == 'physical':533 if device.type == 'physical':
@@ -551,22 +551,14 @@ class MachineHandler(NodeHandler):
551 params.get('mount_options', ''))551 params.get('mount_options', ''))
552552
553 def delete_disk(self, params):553 def delete_disk(self, params):
554 # Only admin users can perform delete.554 node = self._get_node_or_permission_error(params)
555 if not reload_object(self.user).is_superuser:
556 raise HandlerPermissionError()
557
558 node = self.get_object(params)
559 block_id = params.get('block_id')555 block_id = params.get('block_id')
560 if block_id is not None:556 if block_id is not None:
561 block_device = BlockDevice.objects.get(id=block_id, node=node)557 block_device = BlockDevice.objects.get(id=block_id, node=node)
562 block_device.delete()558 block_device.delete()
563559
564 def delete_partition(self, params):560 def delete_partition(self, params):
565 # Only admin users can perform delete.561 node = self._get_node_or_permission_error(params)
566 if not reload_object(self.user).is_superuser:
567 raise HandlerPermissionError()
568
569 node = self.get_object(params)
570 partition_id = params.get('partition_id')562 partition_id = params.get('partition_id')
571 if partition_id is not None:563 if partition_id is not None:
572 partition = Partition.objects.get(564 partition = Partition.objects.get(
@@ -574,11 +566,7 @@ class MachineHandler(NodeHandler):
574 partition.delete()566 partition.delete()
575567
576 def delete_volume_group(self, params):568 def delete_volume_group(self, params):
577 # Only admin users can perform delete.569 node = self._get_node_or_permission_error(params)
578 if not reload_object(self.user).is_superuser:
579 raise HandlerPermissionError()
580
581 node = self.get_object(params)
582 volume_group_id = params.get('volume_group_id')570 volume_group_id = params.get('volume_group_id')
583 if volume_group_id is not None:571 if volume_group_id is not None:
584 volume_group = VolumeGroup.objects.get(id=volume_group_id)572 volume_group = VolumeGroup.objects.get(id=volume_group_id)
@@ -587,11 +575,7 @@ class MachineHandler(NodeHandler):
587 volume_group.delete()575 volume_group.delete()
588576
589 def delete_cache_set(self, params):577 def delete_cache_set(self, params):
590 # Only admin users can perform delete.578 node = self._get_node_or_permission_error(params)
591 if not reload_object(self.user).is_superuser:
592 raise HandlerPermissionError()
593
594 node = self.get_object(params)
595 cache_set_id = params.get('cache_set_id')579 cache_set_id = params.get('cache_set_id')
596 if cache_set_id is not None:580 if cache_set_id is not None:
597 cache_set = CacheSet.objects.get(id=cache_set_id)581 cache_set = CacheSet.objects.get(id=cache_set_id)
@@ -600,10 +584,7 @@ class MachineHandler(NodeHandler):
600 cache_set.delete()584 cache_set.delete()
601585
602 def delete_filesystem(self, params):586 def delete_filesystem(self, params):
603 # Only admin users can perform delete.587 node = self._get_node_or_permission_error(params)
604 if not reload_object(self.user).is_superuser:
605 raise HandlerPermissionError()
606 node = self.get_object(params)
607 blockdevice_id = params.get('blockdevice_id')588 blockdevice_id = params.get('blockdevice_id')
608 partition_id = params.get('partition_id')589 partition_id = params.get('partition_id')
609 filesystem_id = params.get('filesystem_id')590 filesystem_id = params.get('filesystem_id')
@@ -618,11 +599,7 @@ class MachineHandler(NodeHandler):
618599
619 def create_partition(self, params):600 def create_partition(self, params):
620 """Create a partition."""601 """Create a partition."""
621 # Only admin users can perform delete.602 node = self._get_node_or_permission_error(params)
622 if not reload_object(self.user).is_superuser:
623 raise HandlerPermissionError()
624
625 node = self.get_object(params)
626 disk_obj = BlockDevice.objects.get(id=params['block_id'], node=node)603 disk_obj = BlockDevice.objects.get(id=params['block_id'], node=node)
627 form = AddPartitionForm(604 form = AddPartitionForm(
628 disk_obj, {605 disk_obj, {
@@ -640,11 +617,7 @@ class MachineHandler(NodeHandler):
640617
641 def create_cache_set(self, params):618 def create_cache_set(self, params):
642 """Create a cache set."""619 """Create a cache set."""
643 # Only admin users can perform delete.620 node = self._get_node_or_permission_error(params)
644 if not reload_object(self.user).is_superuser:
645 raise HandlerPermissionError()
646
647 node = self.get_object(params)
648 block_id = params.get('block_id')621 block_id = params.get('block_id')
649 partition_id = params.get('partition_id')622 partition_id = params.get('partition_id')
650623
@@ -665,11 +638,7 @@ class MachineHandler(NodeHandler):
665638
666 def create_bcache(self, params):639 def create_bcache(self, params):
667 """Create a bcache."""640 """Create a bcache."""
668 # Only admin users can perform delete.641 node = self._get_node_or_permission_error(params)
669 if not reload_object(self.user).is_superuser:
670 raise HandlerPermissionError()
671
672 node = self.get_object(params)
673 block_id = params.get('block_id')642 block_id = params.get('block_id')
674 partition_id = params.get('partition_id')643 partition_id = params.get('partition_id')
675644
@@ -701,11 +670,7 @@ class MachineHandler(NodeHandler):
701670
702 def create_raid(self, params):671 def create_raid(self, params):
703 """Create a RAID."""672 """Create a RAID."""
704 # Only admin users can perform delete.673 node = self._get_node_or_permission_error(params)
705 if not reload_object(self.user).is_superuser:
706 raise HandlerPermissionError()
707
708 node = self.get_object(params)
709 form = CreateRaidForm(node=node, data=params)674 form = CreateRaidForm(node=node, data=params)
710 if not form.is_valid():675 if not form.is_valid():
711 raise HandlerError(form.errors)676 raise HandlerError(form.errors)
@@ -720,11 +685,7 @@ class MachineHandler(NodeHandler):
720685
721 def create_volume_group(self, params):686 def create_volume_group(self, params):
722 """Create a volume group."""687 """Create a volume group."""
723 # Only admin users can perform delete.688 node = self._get_node_or_permission_error(params)
724 if not reload_object(self.user).is_superuser:
725 raise HandlerPermissionError()
726
727 node = self.get_object(params)
728 form = CreateVolumeGroupForm(node=node, data=params)689 form = CreateVolumeGroupForm(node=node, data=params)
729 if not form.is_valid():690 if not form.is_valid():
730 raise HandlerError(form.errors)691 raise HandlerError(form.errors)
@@ -733,11 +694,7 @@ class MachineHandler(NodeHandler):
733694
734 def create_logical_volume(self, params):695 def create_logical_volume(self, params):
735 """Create a logical volume."""696 """Create a logical volume."""
736 # Only admin users can perform delete.697 node = self._get_node_or_permission_error(params)
737 if not reload_object(self.user).is_superuser:
738 raise HandlerPermissionError()
739
740 node = self.get_object(params)
741 volume_group = VolumeGroup.objects.get(id=params['volume_group_id'])698 volume_group = VolumeGroup.objects.get(id=params['volume_group_id'])
742 if volume_group.get_node() != node:699 if volume_group.get_node() != node:
743 raise VolumeGroup.DoesNotExist()700 raise VolumeGroup.DoesNotExist()
@@ -759,11 +716,7 @@ class MachineHandler(NodeHandler):
759716
760 def set_boot_disk(self, params):717 def set_boot_disk(self, params):
761 """Set the disk as the boot disk."""718 """Set the disk as the boot disk."""
762 # Only admin users can perform delete.719 node = self._get_node_or_permission_error(params)
763 if not reload_object(self.user).is_superuser:
764 raise HandlerPermissionError()
765
766 node = self.get_object(params)
767 device = BlockDevice.objects.get(720 device = BlockDevice.objects.get(
768 id=params['block_id'], node=node).actual_instance721 id=params['block_id'], node=node).actual_instance
769 if device.type != 'physical':722 if device.type != 'physical':
@@ -803,11 +756,7 @@ class MachineHandler(NodeHandler):
803756
804 def create_physical(self, params):757 def create_physical(self, params):
805 """Create physical interface."""758 """Create physical interface."""
806 # Only admin users can perform create.759 node = self._get_node_or_permission_error(params)
807 if not reload_object(self.user).is_superuser:
808 raise HandlerPermissionError()
809
810 node = self.get_object(params)
811 form = PhysicalInterfaceForm(node=node, data=params)760 form = PhysicalInterfaceForm(node=node, data=params)
812 if form.is_valid():761 if form.is_valid():
813 interface = form.save()762 interface = form.save()
@@ -818,11 +767,7 @@ class MachineHandler(NodeHandler):
818767
819 def create_vlan(self, params):768 def create_vlan(self, params):
820 """Create VLAN interface."""769 """Create VLAN interface."""
821 # Only admin users can perform create.770 node = self._get_node_or_permission_error(params)
822 if not reload_object(self.user).is_superuser:
823 raise HandlerPermissionError()
824
825 node = self.get_object(params)
826 params['parents'] = [params.pop('parent')]771 params['parents'] = [params.pop('parent')]
827 form = VLANInterfaceForm(node=node, data=params)772 form = VLANInterfaceForm(node=node, data=params)
828 if form.is_valid():773 if form.is_valid():
@@ -834,11 +779,7 @@ class MachineHandler(NodeHandler):
834779
835 def create_bond(self, params):780 def create_bond(self, params):
836 """Create bond interface."""781 """Create bond interface."""
837 # Only admin users can perform create.782 node = self._get_node_or_permission_error(params)
838 if not reload_object(self.user).is_superuser:
839 raise HandlerPermissionError()
840
841 node = self.get_object(params)
842 form = BondInterfaceForm(node=node, data=params)783 form = BondInterfaceForm(node=node, data=params)
843 if form.is_valid():784 if form.is_valid():
844 interface = form.save()785 interface = form.save()
@@ -849,11 +790,7 @@ class MachineHandler(NodeHandler):
849790
850 def create_bridge(self, params):791 def create_bridge(self, params):
851 """Create bridge interface."""792 """Create bridge interface."""
852 # Only admin users can perform create.793 node = self._get_node_or_permission_error(params)
853 if not reload_object(self.user).is_superuser:
854 raise HandlerPermissionError()
855
856 node = self.get_object(params)
857 if node.status == NODE_STATUS.ALLOCATED:794 if node.status == NODE_STATUS.ALLOCATED:
858 form = AcquiredBridgeInterfaceForm(node=node, data=params)795 form = AcquiredBridgeInterfaceForm(node=node, data=params)
859 else:796 else:
@@ -867,11 +804,7 @@ class MachineHandler(NodeHandler):
867804
868 def update_interface(self, params):805 def update_interface(self, params):
869 """Update the interface."""806 """Update the interface."""
870 # Only admin users can perform update.807 node = self._get_node_or_permission_error(params)
871 if not reload_object(self.user).is_superuser:
872 raise HandlerPermissionError()
873
874 node = self.get_object(params)
875 interface = Interface.objects.get(node=node, id=params["interface_id"])808 interface = Interface.objects.get(node=node, id=params["interface_id"])
876 if node.status == NODE_STATUS.DEPLOYED:809 if node.status == NODE_STATUS.DEPLOYED:
877 interface_form = DeployedInterfaceForm810 interface_form = DeployedInterfaceForm
@@ -889,21 +822,13 @@ class MachineHandler(NodeHandler):
889822
890 def delete_interface(self, params):823 def delete_interface(self, params):
891 """Delete the interface."""824 """Delete the interface."""
892 # Only admin users can perform delete.825 node = self._get_node_or_permission_error(params)
893 if not reload_object(self.user).is_superuser:
894 raise HandlerPermissionError()
895
896 node = self.get_object(params)
897 interface = Interface.objects.get(node=node, id=params["interface_id"])826 interface = Interface.objects.get(node=node, id=params["interface_id"])
898 interface.delete()827 interface.delete()
899828
900 def link_subnet(self, params):829 def link_subnet(self, params):
901 """Create or update the link."""830 """Create or update the link."""
902 # Only admin users can perform update.831 node = self._get_node_or_permission_error(params)
903 if not reload_object(self.user).is_superuser:
904 raise HandlerPermissionError()
905
906 node = self.get_object(params)
907 interface = Interface.objects.get(node=node, id=params["interface_id"])832 interface = Interface.objects.get(node=node, id=params["interface_id"])
908 subnet = None833 subnet = None
909 if "subnet" in params:834 if "subnet" in params:
@@ -923,11 +848,7 @@ class MachineHandler(NodeHandler):
923848
924 def unlink_subnet(self, params):849 def unlink_subnet(self, params):
925 """Delete the link."""850 """Delete the link."""
926 # Only admin users can perform unlink.851 node = self._get_node_or_permission_error(params)
927 if not reload_object(self.user).is_superuser:
928 raise HandlerPermissionError()
929
930 node = self.get_object(params)
931 interface = Interface.objects.get(node=node, id=params["interface_id"])852 interface = Interface.objects.get(node=node, id=params["interface_id"])
932 interface.unlink_subnet_by_id(params["link_id"])853 interface.unlink_subnet_by_id(params["link_id"])
933854
@@ -958,3 +879,9 @@ class MachineHandler(NodeHandler):
958 d.addErrback(eb_error)879 d.addErrback(eb_error)
959 d.addCallback(partial(deferToDatabase, update_state))880 d.addCallback(partial(deferToDatabase, update_state))
960 return d881 return d
882
883 def _get_node_or_permission_error(self, params):
884 node = self.get_object(params)
885 if not reload_object(self.user).is_superuser or node.locked:
886 raise HandlerPermissionError()
887 return node
diff --git a/src/maasserver/websockets/handlers/tests/test_general.py b/src/maasserver/websockets/handlers/tests/test_general.py
index 8e4139b..f530de8 100644
--- a/src/maasserver/websockets/handlers/tests/test_general.py
+++ b/src/maasserver/websockets/handlers/tests/test_general.py
@@ -164,7 +164,8 @@ class TestGeneralHandler(MAASServerTestCase):
164 self.assertItemsEqual(164 self.assertItemsEqual(
165 ['release', 'mark-broken', 'on', 'deploy', 'mark-fixed',165 ['release', 'mark-broken', 'on', 'deploy', 'mark-fixed',
166 'commission', 'abort', 'acquire', 'off', 'rescue-mode',166 'commission', 'abort', 'acquire', 'off', 'rescue-mode',
167 'exit-rescue-mode', 'test', 'override-failed-testing'],167 'exit-rescue-mode', 'lock', 'test', 'override-failed-testing',
168 'unlock'],
168 [action['name'] for action in handler.machine_actions({})])169 [action['name'] for action in handler.machine_actions({})])
169170
170 def test_device_actions_for_admin(self):171 def test_device_actions_for_admin(self):
diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py
index ea45b78..ceef773 100644
--- a/src/maasserver/websockets/handlers/tests/test_machine.py
+++ b/src/maasserver/websockets/handlers/tests/test_machine.py
@@ -1799,10 +1799,19 @@ class TestMachineHandler(MAASServerTestCase):
17991799
1800 def test_update_raise_permissions_error_for_non_admin(self):1800 def test_update_raise_permissions_error_for_non_admin(self):
1801 user = factory.make_User()1801 user = factory.make_User()
1802 node = factory.make_Node()
1803 handler = MachineHandler(user, {})
1804 self.assertRaises(
1805 HandlerPermissionError,
1806 handler.update, {'system_id': node.system_id})
1807
1808 def test_update_raise_permissions_error_for_locked_node(self):
1809 user = factory.make_admin()
1810 node = factory.make_Node(locked=True)
1802 handler = MachineHandler(user, {})1811 handler = MachineHandler(user, {})
1803 self.assertRaises(1812 self.assertRaises(
1804 HandlerPermissionError,1813 HandlerPermissionError,
1805 handler.update, {})1814 handler.update, {'system_id': node.system_id})
18061815
1807 def test_update_raises_validation_error_for_invalid_architecture(self):1816 def test_update_raises_validation_error_for_invalid_architecture(self):
1808 user = factory.make_admin()1817 user = factory.make_admin()
@@ -1980,6 +1989,18 @@ class TestMachineHandler(MAASServerTestCase):
1980 self.assertEqual(new_name, block_device.name)1989 self.assertEqual(new_name, block_device.name)
1981 self.assertItemsEqual(new_tags, block_device.tags)1990 self.assertItemsEqual(new_tags, block_device.tags)
19821991
1992 def test_update_disk_locked_raises_permission_error(self):
1993 user = factory.make_admin()
1994 handler = MachineHandler(user, {})
1995 node = factory.make_Node(locked=True)
1996 block_device = factory.make_PhysicalBlockDevice(node=node)
1997 new_name = factory.make_name("new")
1998 params = {
1999 'system_id': node.system_id,
2000 'block_id': block_device.id,
2001 'name': new_name}
2002 self.assertRaises(HandlerPermissionError, handler.update_disk, params)
2003
1983 def test_delete_disk(self):2004 def test_delete_disk(self):
1984 user = factory.make_admin()2005 user = factory.make_admin()
1985 handler = MachineHandler(user, {})2006 handler = MachineHandler(user, {})
@@ -1995,6 +2016,16 @@ class TestMachineHandler(MAASServerTestCase):
1995 })2016 })
1996 self.assertIsNone(reload_object(block_device))2017 self.assertIsNone(reload_object(block_device))
19972018
2019 def test_delete_disk_locked_raises_permission_error(self):
2020 user = factory.make_admin()
2021 handler = MachineHandler(user, {})
2022 node = factory.make_Node(locked=True)
2023 block_device = factory.make_PhysicalBlockDevice(node=node)
2024 params = {
2025 'system_id': node.system_id,
2026 'block_id': block_device.id}
2027 self.assertRaises(HandlerPermissionError, handler.delete_disk, params)
2028
1998 def test_delete_partition(self):2029 def test_delete_partition(self):
1999 user = factory.make_admin()2030 user = factory.make_admin()
2000 handler = MachineHandler(user, {})2031 handler = MachineHandler(user, {})
@@ -2010,6 +2041,17 @@ class TestMachineHandler(MAASServerTestCase):
2010 })2041 })
2011 self.assertIsNone(reload_object(partition))2042 self.assertIsNone(reload_object(partition))
20122043
2044 def test_delete_partition_locked_raises_permission_error(self):
2045 user = factory.make_admin()
2046 handler = MachineHandler(user, {})
2047 node = factory.make_Node(locked=True)
2048 partition = factory.make_Partition(node=node)
2049 params = {
2050 'system_id': node.system_id,
2051 'partition_id': partition.id}
2052 self.assertRaises(
2053 HandlerPermissionError, handler.delete_partition, params)
2054
2013 def test_delete_volume_group(self):2055 def test_delete_volume_group(self):
2014 user = factory.make_admin()2056 user = factory.make_admin()
2015 handler = MachineHandler(user, {})2057 handler = MachineHandler(user, {})
@@ -2026,6 +2068,18 @@ class TestMachineHandler(MAASServerTestCase):
2026 })2068 })
2027 self.assertIsNone(reload_object(volume_group))2069 self.assertIsNone(reload_object(volume_group))
20282070
2071 def test_delete_volume_group_locked_raises_permission_error(self):
2072 user = factory.make_admin()
2073 handler = MachineHandler(user, {})
2074 node = factory.make_Node(locked=True)
2075 volume_group = factory.make_FilesystemGroup(
2076 node=node, group_type=FILESYSTEM_GROUP_TYPE.LVM_VG)
2077 params = {
2078 'system_id': node.system_id,
2079 'volume_group_id': volume_group.id}
2080 self.assertRaises(
2081 HandlerPermissionError, handler.delete_volume_group, params)
2082
2029 def test_delete_cache_set(self):2083 def test_delete_cache_set(self):
2030 user = factory.make_admin()2084 user = factory.make_admin()
2031 handler = MachineHandler(user, {})2085 handler = MachineHandler(user, {})
@@ -2041,6 +2095,17 @@ class TestMachineHandler(MAASServerTestCase):
2041 })2095 })
2042 self.assertIsNone(reload_object(cache_set))2096 self.assertIsNone(reload_object(cache_set))
20432097
2098 def test_delete_cache_set_locked_raises_permission_error(self):
2099 user = factory.make_admin()
2100 handler = MachineHandler(user, {})
2101 node = factory.make_Node(locked=True)
2102 cache_set = factory.make_CacheSet(node=node)
2103 params = {
2104 'system_id': node.system_id,
2105 'cache_set_id': cache_set.id}
2106 self.assertRaises(
2107 HandlerPermissionError, handler.delete_cache_set, params)
2108
2044 def test_delete_filesystem_deletes_blockdevice_filesystem(self):2109 def test_delete_filesystem_deletes_blockdevice_filesystem(self):
2045 user = factory.make_admin()2110 user = factory.make_admin()
2046 handler = MachineHandler(user, {})2111 handler = MachineHandler(user, {})
@@ -2079,6 +2144,20 @@ class TestMachineHandler(MAASServerTestCase):
2079 self.assertIsNone(reload_object(filesystem))2144 self.assertIsNone(reload_object(filesystem))
2080 self.assertIsNotNone(reload_object(partition))2145 self.assertIsNotNone(reload_object(partition))
20812146
2147 def test_delete_filesystem_locked_raises_permission_error(self):
2148 user = factory.make_admin()
2149 handler = MachineHandler(user, {})
2150 node = factory.make_Node(locked=True)
2151 partition = factory.make_Partition(node=node)
2152 filesystem = factory.make_Filesystem(
2153 partition=partition, fstype=FILESYSTEM_TYPE.EXT4)
2154 params = {
2155 'system_id': node.system_id,
2156 'partition_id': partition.id,
2157 'filesystem_id': filesystem.id}
2158 self.assertRaises(
2159 HandlerPermissionError, handler.delete_filesystem, params)
2160
2082 def test_create_partition(self):2161 def test_create_partition(self):
2083 user = factory.make_admin()2162 user = factory.make_admin()
2084 handler = MachineHandler(user, {})2163 handler = MachineHandler(user, {})
@@ -2142,6 +2221,29 @@ class TestMachineHandler(MAASServerTestCase):
2142 fstype=fstype, mount_point=mount_point,2221 fstype=fstype, mount_point=mount_point,
2143 mount_options=mount_options))2222 mount_options=mount_options))
21442223
2224 def test_create_partition_locked_raises_permission_error(self):
2225 user = factory.make_admin()
2226 handler = MachineHandler(user, {})
2227 node = factory.make_Node(locked=True)
2228 block_device = factory.make_BlockDevice(node=node)
2229 partition_table = factory.make_PartitionTable(
2230 block_device=block_device, node=node)
2231 partition_table = factory.make_PartitionTable(
2232 block_device=block_device, node=node)
2233 size = partition_table.block_device.size // 2
2234 fstype = factory.pick_filesystem_type()
2235 mount_point = factory.make_absolute_path()
2236 mount_options = factory.make_name("options")
2237 params = {
2238 'system_id': node.system_id,
2239 'block_id': partition_table.block_device_id,
2240 'partition_size': size,
2241 'fstype': fstype,
2242 'mount_point': mount_point,
2243 'mount_options': mount_options}
2244 self.assertRaises(
2245 HandlerPermissionError, handler.create_partition, params)
2246
2145 def test_create_cache_set_for_partition(self):2247 def test_create_cache_set_for_partition(self):
2146 user = factory.make_admin()2248 user = factory.make_admin()
2147 handler = MachineHandler(user, {})2249 handler = MachineHandler(user, {})
@@ -2171,6 +2273,17 @@ class TestMachineHandler(MAASServerTestCase):
2171 self.assertEqual(2273 self.assertEqual(
2172 block_device.id, cache_set.get_filesystem().block_device.id)2274 block_device.id, cache_set.get_filesystem().block_device.id)
21732275
2276 def test_create_cache_set_locked_raises_permission_error(self):
2277 user = factory.make_admin()
2278 handler = MachineHandler(user, {})
2279 node = factory.make_Node(locked=True)
2280 partition = factory.make_Partition(node=node)
2281 params = {
2282 'system_id': node.system_id,
2283 'partition_id': partition.id}
2284 self.assertRaises(
2285 HandlerPermissionError, handler.create_cache_set, params)
2286
2174 def test_create_bcache_for_partition(self):2287 def test_create_bcache_for_partition(self):
2175 user = factory.make_admin()2288 user = factory.make_admin()
2176 handler = MachineHandler(user, {})2289 handler = MachineHandler(user, {})
@@ -2303,6 +2416,29 @@ class TestMachineHandler(MAASServerTestCase):
2303 fstype=fstype, mount_point=mount_point,2416 fstype=fstype, mount_point=mount_point,
2304 mount_options=mount_options))2417 mount_options=mount_options))
23052418
2419 def test_create_bcache_set_locked_raises_permission_error(self):
2420 user = factory.make_admin()
2421 handler = MachineHandler(user, {})
2422 node = factory.make_Node(locked=True)
2423 block_device = factory.make_PhysicalBlockDevice(node=node)
2424 name = factory.make_name("bcache")
2425 cache_set = factory.make_CacheSet(node=node)
2426 cache_mode = factory.pick_enum(CACHE_MODE_TYPE)
2427 fstype = factory.pick_filesystem_type()
2428 mount_point = factory.make_absolute_path()
2429 mount_options = factory.make_name("options")
2430 params = {
2431 'system_id': node.system_id,
2432 'block_id': block_device.id,
2433 'name': name,
2434 'cache_set': cache_set.id,
2435 'cache_mode': cache_mode,
2436 'fstype': fstype,
2437 'mount_point': mount_point,
2438 'mount_options': mount_options}
2439 self.assertRaises(
2440 HandlerPermissionError, handler.create_bcache, params)
2441
2306 def test_create_raid(self):2442 def test_create_raid(self):
2307 user = factory.make_admin()2443 user = factory.make_admin()
2308 handler = MachineHandler(user, {})2444 handler = MachineHandler(user, {})
@@ -2363,6 +2499,20 @@ class TestMachineHandler(MAASServerTestCase):
2363 fstype=fstype, mount_point=mount_point,2499 fstype=fstype, mount_point=mount_point,
2364 mount_options=mount_options))2500 mount_options=mount_options))
23652501
2502 def test_create_raid_locked_raises_permission_error(self):
2503 user = factory.make_admin()
2504 handler = MachineHandler(user, {})
2505 node = factory.make_Node(locked=True)
2506 disk0 = factory.make_PhysicalBlockDevice(node=node)
2507 disk1 = factory.make_PhysicalBlockDevice(node=node)
2508 params = {
2509 'system_id': node.system_id,
2510 'name': factory.make_name('md'),
2511 'level': 'raid-1',
2512 'block_devices': [disk0.id, disk1.id]}
2513 self.assertRaises(
2514 HandlerPermissionError, handler.create_raid, params)
2515
2366 def test_create_volume_group(self):2516 def test_create_volume_group(self):
2367 user = factory.make_admin()2517 user = factory.make_admin()
2368 handler = MachineHandler(user, {})2518 handler = MachineHandler(user, {})
@@ -2439,6 +2589,21 @@ class TestMachineHandler(MAASServerTestCase):
2439 fstype=fstype, mount_point=mount_point,2589 fstype=fstype, mount_point=mount_point,
2440 mount_options=mount_options))2590 mount_options=mount_options))
24412591
2592 def test_create_logical_volume_locked_raises_permission_error(self):
2593 user = factory.make_admin()
2594 handler = MachineHandler(user, {})
2595 node = factory.make_Node(locked=True)
2596 volume_group = factory.make_FilesystemGroup(
2597 group_type=FILESYSTEM_GROUP_TYPE.LVM_VG, node=node)
2598 size = volume_group.get_lvm_free_space()
2599 params = {
2600 'system_id': node.system_id,
2601 'name': factory.make_name("lv"),
2602 'volume_group_id': volume_group.id,
2603 'size': size}
2604 self.assertRaises(
2605 HandlerPermissionError, handler.create_logical_volume, params)
2606
2442 def test_set_boot_disk(self):2607 def test_set_boot_disk(self):
2443 user = factory.make_admin()2608 user = factory.make_admin()
2444 handler = MachineHandler(user, {})2609 handler = MachineHandler(user, {})
@@ -2464,6 +2629,17 @@ class TestMachineHandler(MAASServerTestCase):
2464 self.assertEqual(2629 self.assertEqual(
2465 str(error), "Only a physical disk can be set as the boot disk.")2630 str(error), "Only a physical disk can be set as the boot disk.")
24662631
2632 def test_set_boot_disk_locked_raises_permission_error(self):
2633 user = factory.make_admin()
2634 handler = MachineHandler(user, {})
2635 node = factory.make_Node(locked=True)
2636 boot_disk = factory.make_PhysicalBlockDevice(node=node)
2637 params = {
2638 'system_id': node.system_id,
2639 'block_id': boot_disk.id}
2640 self.assertRaises(
2641 HandlerPermissionError, handler.set_boot_disk, params)
2642
2467 def test_update_raise_HandlerError_if_tag_has_definition(self):2643 def test_update_raise_HandlerError_if_tag_has_definition(self):
2468 user = factory.make_admin()2644 user = factory.make_admin()
2469 handler = MachineHandler(user, {})2645 handler = MachineHandler(user, {})
@@ -2625,6 +2801,19 @@ class TestMachineHandler(MAASServerTestCase):
2625 node=node, type=INTERFACE_TYPE.VLAN, parents=interface))2801 node=node, type=INTERFACE_TYPE.VLAN, parents=interface))
2626 self.assertIsNotNone(vlan_interface)2802 self.assertIsNotNone(vlan_interface)
26272803
2804 def test_create_physical_locked_raises_permission_error(self):
2805 user = factory.make_admin()
2806 node = factory.make_Node(locked=True)
2807 handler = MachineHandler(user, {})
2808 vlan = factory.make_VLAN()
2809 params = {
2810 "system_id": node.system_id,
2811 "name": factory.make_name("eth"),
2812 "mac_address": factory.make_mac_address(),
2813 "vlan": vlan.id}
2814 self.assertRaises(
2815 HandlerPermissionError, handler.create_physical, params)
2816
2628 def test_create_vlan_creates_link_auto(self):2817 def test_create_vlan_creates_link_auto(self):
2629 user = factory.make_admin()2818 user = factory.make_admin()
2630 node = factory.make_Node()2819 node = factory.make_Node()
@@ -2692,6 +2881,23 @@ class TestMachineHandler(MAASServerTestCase):
2692 alloc_type=IPADDRESS_TYPE.STICKY, ip=None, subnet=new_subnet)2881 alloc_type=IPADDRESS_TYPE.STICKY, ip=None, subnet=new_subnet)
2693 self.assertIsNotNone(link_up_ip)2882 self.assertIsNotNone(link_up_ip)
26942883
2884 def test_create_vlan_locked_raises_permission_error(self):
2885 user = factory.make_admin()
2886 node = factory.make_Node(locked=True)
2887 handler = MachineHandler(user, {})
2888 vlan = factory.make_VLAN()
2889 interface = factory.make_Interface(
2890 INTERFACE_TYPE.PHYSICAL, node=node, vlan=vlan)
2891 new_subnet = factory.make_Subnet(vlan=vlan)
2892 params = {
2893 "system_id": node.system_id,
2894 "parent": interface.id,
2895 "vlan": vlan.id,
2896 "mode": INTERFACE_LINK_TYPE.AUTO,
2897 "subnet": new_subnet.id}
2898 self.assertRaises(
2899 HandlerPermissionError, handler.create_vlan, params)
2900
2695 def test_create_bond_creates_bond(self):2901 def test_create_bond_creates_bond(self):
2696 user = factory.make_admin()2902 user = factory.make_admin()
2697 node = factory.make_Node()2903 node = factory.make_Node()
@@ -2729,6 +2935,24 @@ class TestMachineHandler(MAASServerTestCase):
2729 "parents": [nic1.id, nic2.id],2935 "parents": [nic1.id, nic2.id],
2730 })2936 })
27312937
2938 def test_create_bond_locked_raises_permission_error(self):
2939 user = factory.make_admin()
2940 node = factory.make_Node(locked=True)
2941 handler = MachineHandler(user, {})
2942 nic1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
2943 nic2 = factory.make_Interface(
2944 INTERFACE_TYPE.PHYSICAL, node=node, vlan=nic1.vlan)
2945 bond_mode = factory.pick_enum(BOND_MODE)
2946 params = {
2947 "system_id": node.system_id,
2948 "name": factory.make_name("bond"),
2949 "parents": [nic1.id, nic2.id],
2950 "mac_address": "%s" % nic1.mac_address,
2951 "vlan": nic1.vlan.id,
2952 "bond_mode": bond_mode}
2953 self.assertRaises(
2954 HandlerPermissionError, handler.create_bond, params)
2955
2732 def test_create_bridge_creates_bridge(self):2956 def test_create_bridge_creates_bridge(self):
2733 user = factory.make_admin()2957 user = factory.make_admin()
2734 node = factory.make_Node()2958 node = factory.make_Node()
@@ -2765,6 +2989,22 @@ class TestMachineHandler(MAASServerTestCase):
2765 "parents": [nic1.id],2989 "parents": [nic1.id],
2766 })2990 })
27672991
2992 def test_create_bridge_locked_raises_permission_error(self):
2993 user = factory.make_admin()
2994 node = factory.make_Node(locked=True)
2995 handler = MachineHandler(user, {})
2996 nic1 = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
2997 params = {
2998 "system_id": node.system_id,
2999 "name": factory.make_name("br"),
3000 "parents": [nic1.id],
3001 "mac_address": "%s" % nic1.mac_address,
3002 "vlan": nic1.vlan.id,
3003 "bridge_stp": factory.pick_bool(),
3004 "bridge_fd": random.randint(0, 15)}
3005 self.assertRaises(
3006 HandlerPermissionError, handler.create_bridge, params)
3007
2768 def test_update_interface(self):3008 def test_update_interface(self):
2769 user = factory.make_admin()3009 user = factory.make_admin()
2770 node = factory.make_Node()3010 node = factory.make_Node()
@@ -2814,6 +3054,20 @@ class TestMachineHandler(MAASServerTestCase):
2814 "vlan": random.randint(1000, 5000),3054 "vlan": random.randint(1000, 5000),
2815 })3055 })
28163056
3057 def test_update_interface_locked_raises_permission_error(self):
3058 user = factory.make_admin()
3059 node = factory.make_Node(locked=True)
3060 handler = MachineHandler(user, {})
3061 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
3062 handler._script_results = {}
3063 handler._refresh_script_result_cache(node.get_latest_script_results)
3064 params = {
3065 "system_id": node.system_id,
3066 "interface_id": interface.id,
3067 "name": factory.make_name("name")}
3068 self.assertRaises(
3069 HandlerPermissionError, handler.update_interface, params)
3070
2817 def test_delete_interface(self):3071 def test_delete_interface(self):
2818 user = factory.make_admin()3072 user = factory.make_admin()
2819 node = factory.make_Node()3073 node = factory.make_Node()
@@ -2825,6 +3079,17 @@ class TestMachineHandler(MAASServerTestCase):
2825 })3079 })
2826 self.assertIsNone(reload_object(interface))3080 self.assertIsNone(reload_object(interface))
28273081
3082 def test_delete_interface_locked_raises_permission_error(self):
3083 user = factory.make_admin()
3084 node = factory.make_Node(locked=True)
3085 handler = MachineHandler(user, {})
3086 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
3087 params = {
3088 "system_id": node.system_id,
3089 "interface_id": interface.id}
3090 self.assertRaises(
3091 HandlerPermissionError, handler.delete_interface, params)
3092
2828 def test_link_subnet_calls_update_link_by_id_if_link_id(self):3093 def test_link_subnet_calls_update_link_by_id_if_link_id(self):
2829 user = factory.make_admin()3094 user = factory.make_admin()
2830 node = factory.make_Node()3095 node = factory.make_Node()
@@ -2894,6 +3159,22 @@ class TestMachineHandler(MAASServerTestCase):
2894 MockCalledOnceWith(3159 MockCalledOnceWith(
2895 ANY, mode, subnet, ip_address=ip_address))3160 ANY, mode, subnet, ip_address=ip_address))
28963161
3162 def test_link_subnet_locked_raises_permission_error(self):
3163 user = factory.make_admin()
3164 node = factory.make_Node(locked=True)
3165 handler = MachineHandler(user, {})
3166 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
3167 subnet = factory.make_Subnet()
3168 params = {
3169 "system_id": node.system_id,
3170 "interface_id": interface.id,
3171 "link_id": factory.make_StaticIPAddress(interface=interface).id,
3172 "subnet": subnet.id,
3173 "mode": factory.pick_enum(INTERFACE_LINK_TYPE),
3174 "ip_address": factory.make_ip_address()}
3175 self.assertRaises(
3176 HandlerPermissionError, handler.link_subnet, params)
3177
2897 def test_unlink_subnet(self):3178 def test_unlink_subnet(self):
2898 user = factory.make_admin()3179 user = factory.make_admin()
2899 node = factory.make_Node()3180 node = factory.make_Node()
@@ -2901,13 +3182,27 @@ class TestMachineHandler(MAASServerTestCase):
2901 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)3182 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
2902 link_ip = factory.make_StaticIPAddress(3183 link_ip = factory.make_StaticIPAddress(
2903 alloc_type=IPADDRESS_TYPE.AUTO, ip="", interface=interface)3184 alloc_type=IPADDRESS_TYPE.AUTO, ip="", interface=interface)
2904 handler.delete_interface({3185 handler.unlink_subnet({
2905 "system_id": node.system_id,3186 "system_id": node.system_id,
2906 "interface_id": interface.id,3187 "interface_id": interface.id,
2907 "link_id": link_ip.id,3188 "link_id": link_ip.id,
2908 })3189 })
2909 self.assertIsNone(reload_object(link_ip))3190 self.assertIsNone(reload_object(link_ip))
29103191
3192 def test_unlink_subnet_locked_raises_permission_error(self):
3193 user = factory.make_admin()
3194 node = factory.make_Node(locked=True)
3195 handler = MachineHandler(user, {})
3196 interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
3197 link_ip = factory.make_StaticIPAddress(
3198 alloc_type=IPADDRESS_TYPE.AUTO, ip="", interface=interface)
3199 params = {
3200 "system_id": node.system_id,
3201 "interface_id": interface.id,
3202 "link_id": link_ip.id}
3203 self.assertRaises(
3204 HandlerPermissionError, handler.unlink_subnet, params)
3205
2911 def test_get_grouped_storages_parses_blockdevices(self):3206 def test_get_grouped_storages_parses_blockdevices(self):
2912 user = factory.make_User()3207 user = factory.make_User()
2913 node = factory.make_Node(owner=user)3208 node = factory.make_Node(owner=user)
@@ -3045,6 +3340,17 @@ class TestMachineHandlerMountSpecial(MAASServerTestCase):
3045 'mount_point': Equals(["Enter a valid value."]),3340 'mount_point': Equals(["Enter a valid value."]),
3046 }))3341 }))
30473342
3343 def test_locked_raises_permission_error(self):
3344 user = factory.make_admin()
3345 handler = MachineHandler(user, {})
3346 machine = factory.make_Node(locked=True, owner=user)
3347 params = {
3348 'system_id': machine.system_id, 'fstype': FILESYSTEM_TYPE.RAMFS,
3349 'mount_point': factory.make_absolute_path(),
3350 }
3351 self.assertRaises(
3352 HandlerPermissionError, handler.mount_special, params)
3353
30483354
3049class TestMachineHandlerMountSpecialScenarios(MAASServerTestCase):3355class TestMachineHandlerMountSpecialScenarios(MAASServerTestCase):
3050 """Scenario tests for MachineHandler.mount_special."""3356 """Scenario tests for MachineHandler.mount_special."""
@@ -3247,9 +3553,37 @@ class TestMachineHandlerUnmountSpecialScenarios(MAASServerTestCase):
3247 raises_node_state_violation,3553 raises_node_state_violation,
3248 "using status %d on %s" % (status, self.fstype))3554 "using status %d on %s" % (status, self.fstype))
32493555
3556 def test_locked_raises_permission_error(self):
3557 admin = factory.make_admin()
3558 node = factory.make_Node(locked=True, owner=admin)
3559 filesystem = factory.make_Filesystem(
3560 node=node, fstype=self.fstype,
3561 mount_point=factory.make_absolute_path())
3562 handler = MachineHandler(admin, {})
3563 params = {
3564 'system_id': node.system_id,
3565 'mount_point': filesystem.mount_point}
3566 self.assertRaises(
3567 HandlerPermissionError, handler.unmount_special, params)
3568
32503569
3251class TestMachineHandlerUpdateFilesystem(MAASServerTestCase):3570class TestMachineHandlerUpdateFilesystem(MAASServerTestCase):
32523571
3572 def test_locked_raises_permission_error(self):
3573 user = factory.make_admin()
3574 handler = MachineHandler(user, {})
3575 node = factory.make_Node(locked=True)
3576 block_device = factory.make_PhysicalBlockDevice(node=node)
3577 fs = factory.make_Filesystem(block_device=block_device)
3578 params = {
3579 'system_id': node.system_id,
3580 'block_id': block_device.id,
3581 'fstype': fs.fstype,
3582 'mount_point': None,
3583 'mount_options': None}
3584 self.assertRaises(
3585 HandlerPermissionError, handler.update_filesystem, params)
3586
3253 def test_unmount_blockdevice_filesystem(self):3587 def test_unmount_blockdevice_filesystem(self):
3254 user = factory.make_admin()3588 user = factory.make_admin()
3255 handler = MachineHandler(user, {})3589 handler = MachineHandler(user, {})

Subscribers

People subscribed via source and target branches