Merge lp:~ltrager/maas/storage_editable_only_when_ready_or_allocated into lp:~maas-committers/maas/trunk

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: no longer in the source branch.
Merged at revision: 4458
Proposed branch: lp:~ltrager/maas/storage_editable_only_when_ready_or_allocated
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 1361 lines (+504/-91)
9 files modified
src/maasserver/static/js/angular/controllers/node_details.js (+9/-0)
src/maasserver/static/js/angular/controllers/node_details_networking.js (+0/-8)
src/maasserver/static/js/angular/controllers/node_details_storage.js (+51/-17)
src/maasserver/static/js/angular/controllers/tests/test_node_details.js (+16/-0)
src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js (+4/-25)
src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js (+304/-0)
src/maasserver/static/partials/node-details.html (+46/-24)
src/maasserver/websockets/handlers/node.py (+6/-0)
src/maasserver/websockets/handlers/tests/test_node.py (+68/-17)
To merge this branch: bzr merge lp:~ltrager/maas/storage_editable_only_when_ready_or_allocated
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+276355@code.launchpad.net

Commit message

Only allow the storage section to be edited if the node is Ready or Allocated

Description of the change

Only allow the storage section to be edited if the node is Ready or Allocated. This also only allows user to format, mount and umount, change fstype, and change mount_point.

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Have not looked at the code yet, this is just interacting with the branch. I saw the following items that need to be fixed.

1. Select all for file systems is not disabled when not ready.
2. The action buttons "create RAID"..., are not hidden when not ready. (They should be hidden like in networking.)
3. When a node is acquired and I am a super user I am seeing this "Some storage features have been disabled as you are not a super user." Which is incorrect.
4. The action buttons "create RAID"..., are not hidden when a standard user. (They will never be able to perform those so they should be hidden.)

review: Needs Fixing
Revision history for this message
Lee Trager (ltrager) wrote :

Thanks for taking a look at review. I've made the corrections you've asked for. One thing I noticed is that the select all for file systems doesn't really serve a purpose. If you select more than one file system you don't have the ability to perform any action. I think we should either remove it or display a tool bar with one button to unmount all selected options.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Some more things that I noticed using the branch:

1. All the checkboxes should be hidden, not disabled. (That was may fault on the last review.)
2. You say you can modify storage when the node is Broken. This does not match the API, which is only when it is Ready and Allocated.
3. Standard users should only be able to format, partition, unmount, mount, change mount point, and fstype when the node is allocated. They need to own the node before those actions are allowed.

review: Needs Fixing
Revision history for this message
Lee Trager (ltrager) wrote :

I've updated the branch fixing the issues you mentioned. Let me know if you see anything else.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good. Thanks for making all the fixes!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/static/js/angular/controllers/node_details.js'
2--- src/maasserver/static/js/angular/controllers/node_details.js 2015-10-28 20:59:21 +0000
3+++ src/maasserver/static/js/angular/controllers/node_details.js 2015-11-03 04:58:42 +0000
4@@ -477,6 +477,15 @@
5 }
6 }
7
8+ // Return true if user is a super user/
9+ $scope.isSuperUser = function() {
10+ var authUser = UsersManager.getAuthUser();
11+ if(!angular.isObject(authUser)) {
12+ return false;
13+ }
14+ return authUser.is_superuser;
15+ };
16+
17 // Called for autocomplete when the user is typing a tag name.
18 $scope.tagsAutocomplete = function(query) {
19 return TagsManager.autocomplete(query);
20
21=== modified file 'src/maasserver/static/js/angular/controllers/node_details_networking.js'
22--- src/maasserver/static/js/angular/controllers/node_details_networking.js 2015-11-01 00:22:00 +0000
23+++ src/maasserver/static/js/angular/controllers/node_details_networking.js 2015-11-03 04:58:42 +0000
24@@ -462,14 +462,6 @@
25 updateLoaded();
26 };
27
28- // Return true if user is a super user/
29- $scope.isSuperUser = function() {
30- var authUser = UsersManager.getAuthUser();
31- if(!angular.isObject(authUser)) {
32- return false;
33- }
34- return authUser.is_superuser;
35- };
36
37 // Return true if the networking information cannot be edited.
38 // (it can't be changed when the node is in any state other
39
40=== modified file 'src/maasserver/static/js/angular/controllers/node_details_storage.js'
41--- src/maasserver/static/js/angular/controllers/node_details_storage.js 2015-10-30 01:43:06 +0000
42+++ src/maasserver/static/js/angular/controllers/node_details_storage.js 2015-11-03 04:58:42 +0000
43@@ -43,8 +43,8 @@
44 });
45
46 angular.module('MAAS').controller('NodeStorageController', [
47- '$scope', 'NodesManager', 'ConverterService',
48- function($scope, NodesManager, ConverterService) {
49+ '$scope', 'NodesManager', 'ConverterService', 'UsersManager',
50+ function($scope, NodesManager, ConverterService, UsersManager) {
51 var PARTITION_TABLE_EXTRA_SPACE = 3 * 1024 * 1024;
52 var MIN_PARTITION_SIZE = 2 * 1024 * 1024;
53 var MIN_LOGICAL_VOLUME_SIZE = MIN_PARTITION_SIZE;
54@@ -595,10 +595,11 @@
55 // Return true if checkboxes in the filesystem section should be
56 // disabled.
57 $scope.isFilesystemsDisabled = function() {
58- return (
59+ return ((
60 $scope.filesystemMode !== SELECTION_MODE.NONE &&
61 $scope.filesystemMode !== SELECTION_MODE.SINGLE &&
62- $scope.filesystemMode !== SELECTION_MODE.MUTLI);
63+ $scope.filesystemMode !== SELECTION_MODE.MUTLI) ||
64+ $scope.isAllStorageDisabled());
65 };
66
67 // Cancel the current filesystem operation.
68@@ -730,15 +731,17 @@
69 // Return true if checkboxes in the avaiable section should be
70 // disabled.
71 $scope.isAvailableDisabled = function() {
72- return (
73+ return ((
74 $scope.availableMode !== SELECTION_MODE.NONE &&
75 $scope.availableMode !== SELECTION_MODE.SINGLE &&
76- $scope.availableMode !== SELECTION_MODE.MUTLI);
77+ $scope.availableMode !== SELECTION_MODE.MUTLI) ||
78+ $scope.isAllStorageDisabled());
79 };
80
81 // Return true if the disk can be formatted and mounted.
82 $scope.canFormatAndMount = function(disk) {
83- if(disk.type === "lvm-vg" || disk.has_partitions) {
84+ if($scope.isAllStorageDisabled() ||
85+ disk.type === "lvm-vg" || disk.has_partitions) {
86 return false;
87 } else {
88 return true;
89@@ -765,7 +768,9 @@
90
91 // Return true if a partition can be added to disk.
92 $scope.canAddPartition = function(disk) {
93- if(disk.type === "partition" || disk.type === "lvm-vg") {
94+ if(!$scope.isSuperUser() || $scope.isAllStorageDisabled()) {
95+ return false;
96+ } else if(disk.type === "partition" || disk.type === "lvm-vg") {
97 return false;
98 } else if(disk.type === "virtual" &&
99 disk.parent_type === "lvm-vg") {
100@@ -935,7 +940,9 @@
101
102 // Return true if the disk can be deleted.
103 $scope.canDelete = function(disk) {
104- if(disk.type === "lvm-vg") {
105+ if(!$scope.isSuperUser() || $scope.isAllStorageDisabled()) {
106+ return false;
107+ } else if(disk.type === "lvm-vg") {
108 return disk.original.used_size === 0;
109 } else {
110 if(!disk.has_partitions && (
111@@ -1166,10 +1173,12 @@
112 // Return true if checkboxes in the cache sets section should be
113 // disabled.
114 $scope.isCacheSetsDisabled = function() {
115- return (
116+ return ((
117+ $scope.isAllStorageDisabled() &&
118+ !$scope.isSuperUser()) || (
119 $scope.cachesetsMode !== SELECTION_MODE.NONE &&
120 $scope.cachesetsMode !== SELECTION_MODE.SINGLE &&
121- $scope.cachesetsMode !== SELECTION_MODE.MUTLI);
122+ $scope.cachesetsMode !== SELECTION_MODE.MUTLI));
123 };
124
125 // Cancel the current cache set operation.
126@@ -1179,7 +1188,9 @@
127
128 // Can delete the cache set.
129 $scope.canDeleteCacheSet = function(cacheset) {
130- return cacheset.used_by === "";
131+ return (cacheset.used_by === "" &&
132+ !$scope.isAllStorageDisabled() &&
133+ $scope.isSuperUser());
134 };
135
136 // Enter delete mode.
137@@ -1207,7 +1218,7 @@
138
139 // Return true if a cache set can be created.
140 $scope.canCreateCacheSet = function() {
141- if($scope.isAvailableDisabled()) {
142+ if($scope.isAvailableDisabled() || !$scope.isSuperUser()) {
143 return false;
144 }
145
146@@ -1239,7 +1250,7 @@
147
148 // Return true if a bcache can be created.
149 $scope.canCreateBcache = function() {
150- if($scope.isAvailableDisabled()) {
151+ if($scope.isAvailableDisabled() || ! $scope.isSuperUser()) {
152 return false;
153 }
154
155@@ -1349,7 +1360,7 @@
156
157 // Return true if a RAID can be created.
158 $scope.canCreateRAID = function() {
159- if($scope.isAvailableDisabled()) {
160+ if($scope.isAvailableDisabled() || !$scope.isSuperUser()) {
161 return false;
162 }
163
164@@ -1555,7 +1566,7 @@
165
166 // Return true if a volume group can be created.
167 $scope.canCreateVolumeGroup = function() {
168- if($scope.isAvailableDisabled()) {
169+ if($scope.isAvailableDisabled() || !$scope.isSuperUser()) {
170 return false;
171 }
172
173@@ -1726,7 +1737,10 @@
174
175 // Return true when tags can be edited.
176 $scope.canEditTags = function(disk) {
177- return disk.type !== "partition" && disk.type !== "lvm-vg";
178+ return (disk.type !== "partition" &&
179+ disk.type !== "lvm-vg" &&
180+ !$scope.isAllStorageDisabled() &&
181+ $scope.isSuperUser());
182 };
183
184 // Called to enter tag editing mode
185@@ -1753,4 +1767,24 @@
186 disk.tags = disk.$options.tags;
187 disk.$options = {};
188 };
189+
190+ // Returns true if storage cannot be edited.
191+ // (it can't be changed when the node is in any state other
192+ // than Ready or Allocated)
193+ $scope.isAllStorageDisabled = function() {
194+ var authUser = UsersManager.getAuthUser();
195+ if(!angular.isObject(authUser) || !angular.isObject($scope.node) ||
196+ (!authUser.is_superuser &&
197+ authUser.username !== $scope.node.owner)) {
198+ return true;
199+ }else if (angular.isObject($scope.node) &&
200+ ["Ready", "Allocated"].indexOf(
201+ $scope.node.status) === -1) {
202+ // If the node is not ready or allocated, disable storage panel.
203+ return true;
204+ } else {
205+ // The node must be either ready or broken. Enable it.
206+ return false;
207+ }
208+ };
209 }]);
210
211=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details.js'
212--- src/maasserver/static/js/angular/controllers/tests/test_node_details.js 2015-10-28 20:59:21 +0000
213+++ src/maasserver/static/js/angular/controllers/tests/test_node_details.js 2015-11-03 04:58:42 +0000
214@@ -679,6 +679,22 @@
215 });
216 });
217
218+ describe("isSuperUser", function() {
219+ it("returns true if the user is a superuser", function() {
220+ var controller = makeController();
221+ spyOn(UsersManager, "getAuthUser").and.returnValue(
222+ { is_superuser: true });
223+ expect($scope.isSuperUser()).toBe(true);
224+ });
225+
226+ it("returns false if the user is not a superuser", function() {
227+ var controller = makeController();
228+ spyOn(UsersManager, "getAuthUser").and.returnValue(
229+ { is_superuser: false });
230+ expect($scope.isSuperUser()).toBe(false);
231+ });
232+ });
233+
234 describe("getPowerStateClass", function() {
235
236 it("returns blank if no node", function() {
237
238=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js'
239--- src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js 2015-11-01 00:22:00 +0000
240+++ src/maasserver/static/js/angular/controllers/tests/test_node_details_networking.js 2015-11-03 04:58:42 +0000
241@@ -2784,37 +2784,18 @@
242 });
243 });
244
245- describe("isSuperUser", function() {
246- it("returns true if the user is a superuser", function() {
247- var controller = makeController();
248- spyOn(UsersManager, "getAuthUser").and.returnValue(
249- { is_superuser: true });
250- expect($scope.isSuperUser()).toBe(true);
251- });
252-
253- it("returns false if the user is not a superuser", function() {
254- var controller = makeController();
255- spyOn(UsersManager, "getAuthUser").and.returnValue(
256- { is_superuser: false });
257- expect($scope.isSuperUser()).toBe(false);
258- });
259- });
260-
261 describe("isAllNetworkingDisabled", function() {
262 it("returns true if the user is not a superuser and the node is ready",
263 function() {
264 var controller = makeController();
265- spyOn(UsersManager, "getAuthUser").and.returnValue(
266- { is_superuser: false });
267- expect($scope.isSuperUser()).toBe(false);
268+ $scope.isSuperUser = function() { return false; };
269 expect($scope.isAllNetworkingDisabled()).toBe(true);
270 });
271
272 it("return false if the node is Ready and we are a superuser",
273 function() {
274 var controller = makeController();
275- spyOn(UsersManager, "getAuthUser").and.returnValue(
276- { is_superuser: true });
277+ $scope.isSuperUser = function() { return true; };
278 $scope.node.status = "Ready";
279 expect($scope.isAllNetworkingDisabled()).toBe(false);
280 });
281@@ -2822,8 +2803,7 @@
282 it("return false if the node is broken and we are a superuser",
283 function() {
284 var controller = makeController();
285- spyOn(UsersManager, "getAuthUser").and.returnValue(
286- { is_superuser: true });
287+ $scope.isSuperUser = function() { return true; };
288 $scope.node.status = "Broken";
289 expect($scope.isAllNetworkingDisabled()).toBe(false);
290 });
291@@ -2831,8 +2811,7 @@
292 it("return true if the node is deploying and we are a superuser",
293 function() {
294 var controller = makeController();
295- spyOn(UsersManager, "getAuthUser").and.returnValue(
296- { is_superuser: true });
297+ $scope.isSuperUser = function() { return true; };
298 $scope.node.status = "Deploying";
299 expect($scope.isAllNetworkingDisabled()).toBe(true);
300 });
301
302=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js'
303--- src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js 2015-10-30 01:43:06 +0000
304+++ src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js 2015-11-03 04:58:42 +0000
305@@ -669,6 +669,7 @@
306 it("returns false for NONE", function() {
307 var controller = makeController();
308 $scope.filesystemMode = null;
309+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
310
311 expect($scope.isFilesystemsDisabled()).toBe(false);
312 });
313@@ -676,6 +677,7 @@
314 it("returns false for SINGLE", function() {
315 var controller = makeController();
316 $scope.filesystemMode = "single";
317+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
318
319 expect($scope.isFilesystemsDisabled()).toBe(false);
320 });
321@@ -683,6 +685,7 @@
322 it("returns false for MULTI", function() {
323 var controller = makeController();
324 $scope.filesystemMode = "multi";
325+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
326
327 expect($scope.isFilesystemsDisabled()).toBe(false);
328 });
329@@ -690,6 +693,15 @@
330 it("returns true for UNMOUNT", function() {
331 var controller = makeController();
332 $scope.filesystemMode = "unmount";
333+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
334+
335+ expect($scope.isFilesystemsDisabled()).toBe(true);
336+ });
337+
338+ it("returns true when isAllStorageDisabled", function() {
339+ var controller = makeController();
340+ $scope.filesystemMode = "multi";
341+ spyOn($scope, "isAllStorageDisabled").and.returnValue(true);
342
343 expect($scope.isFilesystemsDisabled()).toBe(true);
344 });
345@@ -1100,6 +1112,7 @@
346 it("returns false for NONE", function() {
347 var controller = makeController();
348 $scope.availableMode = null;
349+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
350
351 expect($scope.isAvailableDisabled()).toBe(false);
352 });
353@@ -1107,6 +1120,7 @@
354 it("returns false for SINGLE", function() {
355 var controller = makeController();
356 $scope.availableMode = "single";
357+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
358
359 expect($scope.isAvailableDisabled()).toBe(false);
360 });
361@@ -1114,6 +1128,7 @@
362 it("returns false for MULTI", function() {
363 var controller = makeController();
364 $scope.availableMode = "multi";
365+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
366
367 expect($scope.isAvailableDisabled()).toBe(false);
368 });
369@@ -1121,6 +1136,7 @@
370 it("returns true for UNMOUNT", function() {
371 var controller = makeController();
372 $scope.availableMode = "unmount";
373+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
374
375 expect($scope.isAvailableDisabled()).toBe(true);
376 });
377@@ -1131,18 +1147,21 @@
378 it("returns false if lvm-vg", function() {
379 var controller = makeController();
380 var disk = { type: "lvm-vg" };
381+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
382 expect($scope.canFormatAndMount(disk)).toBe(false);
383 });
384
385 it("returns false if has_partitions", function() {
386 var controller = makeController();
387 var disk = { type: "physical", has_partitions: true };
388+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
389 expect($scope.canFormatAndMount(disk)).toBe(false);
390 });
391
392 it("returns true otherwise", function() {
393 var controller = makeController();
394 var disk = { type: "physical", has_partitions: false };
395+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
396 expect($scope.canFormatAndMount(disk)).toBe(true);
397 });
398 });
399@@ -1183,6 +1202,8 @@
400
401 it("returns false if partition", function() {
402 var controller = makeController();
403+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
404+ $scope.isSuperUser = function() { return true; };
405 expect($scope.canAddPartition({
406 type: "partition"
407 })).toBe(false);
408@@ -1190,6 +1211,8 @@
409
410 it("returns false if lvm-vg", function() {
411 var controller = makeController();
412+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
413+ $scope.isSuperUser = function() { return true; };
414 expect($scope.canAddPartition({
415 type: "lvm-vg"
416 })).toBe(false);
417@@ -1197,6 +1220,8 @@
418
419 it("returns false if logical volume", function() {
420 var controller = makeController();
421+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
422+ $scope.isSuperUser = function() { return true; };
423 expect($scope.canAddPartition({
424 type: "virtual",
425 parent_type: "lvm-vg"
426@@ -1205,6 +1230,8 @@
427
428 it("returns false if formatted", function() {
429 var controller = makeController();
430+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
431+ $scope.isSuperUser = function() { return true; };
432 expect($scope.canAddPartition({
433 type: "physical",
434 fstype: "ext4"
435@@ -1223,6 +1250,8 @@
436 block_size: 1024
437 }
438 };
439+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
440+ $scope.isSuperUser = function() { return true; };
441 expect($scope.canAddPartition(disk)).toBe(false);
442 });
443
444@@ -1238,9 +1267,43 @@
445 block_size: 1024
446 }
447 };
448+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
449+ $scope.isSuperUser = function() { return true; };
450 expect($scope.canAddPartition(disk)).toBe(false);
451 });
452
453+ it("returns false if not super user", function() {
454+ var controller = makeController();
455+ var disk = {
456+ type: "physical",
457+ fstype: "",
458+ original: {
459+ partition_table_type: null,
460+ available_size: 10 * 1024 * 1024,
461+ block_size: 1024
462+ }
463+ };
464+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
465+ $scope.isSuperUser = function() { return false; };
466+ expect($scope.canAddPartition(disk)).toBe(false);
467+ });
468+
469+ it("returns false if isAllStorageDisabled", function() {
470+ var controller = makeController();
471+ var disk = {
472+ type: "physical",
473+ fstype: "",
474+ original: {
475+ partition_table_type: null,
476+ available_size: 10 * 1024 * 1024,
477+ block_size: 1024
478+ }
479+ };
480+ spyOn($scope, "isAllStorageDisabled").and.returnValue(true);
481+ $scope.isSuperUser = function() { return true; };
482+ expect($scope.canAddPartition(disk)).toBe(false);
483+ });
484+
485 it("returns true otherwise", function() {
486 var controller = makeController();
487 var disk = {
488@@ -1252,6 +1315,8 @@
489 block_size: 1024
490 }
491 };
492+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
493+ $scope.isSuperUser = function() { return true; };
494 expect($scope.canAddPartition(disk)).toBe(true);
495 });
496 });
497@@ -1706,10 +1771,44 @@
498 used_size: 0
499 }
500 };
501+ $scope.isSuperUser = function() { return true; };
502+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
503
504 expect($scope.canDelete(disk)).toBe(true);
505 });
506
507+ it("returns false if not super user", function() {
508+ var controller = makeController();
509+ var disk = {
510+ type: "lvm-vg",
511+ fstype: null,
512+ has_partitions: false,
513+ original: {
514+ used_size: 0
515+ }
516+ };
517+ $scope.isSuperUser = function() { return false; };
518+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
519+
520+ expect($scope.canDelete(disk)).toBe(false);
521+ });
522+
523+ it("returns false if isAllStorageDisabled", function() {
524+ var controller = makeController();
525+ var disk = {
526+ type: "lvm-vg",
527+ fstype: null,
528+ has_partitions: false,
529+ original: {
530+ used_size: 0
531+ }
532+ };
533+ $scope.isSuperUser = function() { return true; };
534+ spyOn($scope, "isAllStorageDisabled").and.returnValue(true);
535+
536+ expect($scope.canDelete(disk)).toBe(false);
537+ });
538+
539 it("returns false if volume group used", function() {
540 var controller = makeController();
541 var disk = {
542@@ -1720,6 +1819,8 @@
543 used_size: makeInteger(100, 10000)
544 }
545 };
546+ $scope.isSuperUser = function() { return true; };
547+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
548
549 expect($scope.canDelete(disk)).toBe(false);
550 });
551@@ -1727,6 +1828,8 @@
552 it("returns true if fstype is null", function() {
553 var controller = makeController();
554 var disk = { fstype: null, has_partitions: false };
555+ $scope.isSuperUser = function() { return true; };
556+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
557
558 expect($scope.canDelete(disk)).toBe(true);
559 });
560@@ -1734,6 +1837,8 @@
561 it("returns true if fstype is empty", function() {
562 var controller = makeController();
563 var disk = { fstype: "", has_partitions: false };
564+ $scope.isSuperUser = function() { return true; };
565+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
566
567 expect($scope.canDelete(disk)).toBe(true);
568 });
569@@ -1741,6 +1846,8 @@
570 it("returns false if has_partitions is true", function() {
571 var controller = makeController();
572 var disk = { fstype: "", has_partitions: true };
573+ $scope.isSuperUser = function() { return true; };
574+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
575
576 expect($scope.canDelete(disk)).toBe(false);
577 });
578@@ -1748,6 +1855,8 @@
579 it("returns false if fstype is not empty", function() {
580 var controller = makeController();
581 var disk = { fstype: "ext4" };
582+ $scope.isSuperUser = function() { return true; };
583+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
584
585 expect($scope.canDelete(disk)).toBe(false);
586 });
587@@ -2377,6 +2486,8 @@
588 it("returns false for NONE", function() {
589 var controller = makeController();
590 $scope.cachesetsMode = null;
591+ $scope.isSuperUser = function() { return true; };
592+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
593
594 expect($scope.isCacheSetsDisabled()).toBe(false);
595 });
596@@ -2384,6 +2495,8 @@
597 it("returns false for SINGLE", function() {
598 var controller = makeController();
599 $scope.cachesetsMode = "single";
600+ $scope.isSuperUser = function() { return true; };
601+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
602
603 expect($scope.isCacheSetsDisabled()).toBe(false);
604 });
605@@ -2391,13 +2504,35 @@
606 it("returns false for MULTI", function() {
607 var controller = makeController();
608 $scope.cachesetsMode = "multi";
609+ $scope.isSuperUser = function() { return true; };
610+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
611
612 expect($scope.isCacheSetsDisabled()).toBe(false);
613 });
614
615+ it("returns true for when not super user", function() {
616+ var controller = makeController();
617+ $scope.cachesetsMode = "delete";
618+ $scope.isSuperUser = function() { return false; };
619+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
620+
621+ expect($scope.isCacheSetsDisabled()).toBe(true);
622+ });
623+
624+ it("returns true for when isAllStorageDisabled", function() {
625+ var controller = makeController();
626+ $scope.cachesetsMode = "delete";
627+ $scope.isSuperUser = function() { return true; };
628+ spyOn($scope, "isAllStorageDisabled").and.returnValue(true);
629+
630+ expect($scope.isCacheSetsDisabled()).toBe(true);
631+ });
632+
633 it("returns true for DELETE", function() {
634 var controller = makeController();
635 $scope.cachesetsMode = "delete";
636+ $scope.isSuperUser = function() { return true; };
637+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
638
639 expect($scope.isCacheSetsDisabled()).toBe(true);
640 });
641@@ -2421,6 +2556,8 @@
642 it("returns true when not being used", function() {
643 var controller = makeController();
644 var cacheset = { used_by: "" };
645+ $scope.isSuperUser = function() { return true; };
646+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
647
648 expect($scope.canDeleteCacheSet(cacheset)).toBe(true);
649 });
650@@ -2428,6 +2565,26 @@
651 it("returns false when being used", function() {
652 var controller = makeController();
653 var cacheset = { used_by: "bcache0" };
654+ $scope.isSuperUser = function() { return true; };
655+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
656+
657+ expect($scope.canDeleteCacheSet(cacheset)).toBe(false);
658+ });
659+
660+ it("returns false when not super user", function() {
661+ var controller = makeController();
662+ var cacheset = { used_by: "" };
663+ $scope.isSuperUser = function() { return false; };
664+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
665+
666+ expect($scope.canDeleteCacheSet(cacheset)).toBe(false);
667+ });
668+
669+ it("returns false when isAllStorageDisabled", function() {
670+ var controller = makeController();
671+ var cacheset = { used_by: "" };
672+ $scope.isSuperUser = function() { return true; };
673+ spyOn($scope, "isAllStorageDisabled").and.returnValue(true);
674
675 expect($scope.canDeleteCacheSet(cacheset)).toBe(false);
676 });
677@@ -2490,6 +2647,7 @@
678 it("returns false if isAvailableDisabled returns true", function() {
679 var controller = makeController();
680 spyOn($scope, "isAvailableDisabled").and.returnValue(true);
681+ $scope.isSuperUser = function() { return true; };
682
683 expect($scope.canCreateCacheSet()).toBe(false);
684 });
685@@ -2498,6 +2656,7 @@
686 var controller = makeController();
687 spyOn($scope, "isAvailableDisabled").and.returnValue(false);
688 $scope.available = [ { $selected: true }, { $selected: true }];
689+ $scope.isSuperUser = function() { return true; };
690
691 expect($scope.canCreateCacheSet()).toBe(false);
692 });
693@@ -2511,6 +2670,7 @@
694 $selected: true
695 }
696 ];
697+ $scope.isSuperUser = function() { return true; };
698
699 expect($scope.canCreateCacheSet()).toBe(false);
700 });
701@@ -2525,6 +2685,21 @@
702 $selected: true
703 }
704 ];
705+ $scope.isSuperUser = function() { return true; };
706+
707+ expect($scope.canCreateCacheSet()).toBe(false);
708+ });
709+
710+ it("returns false if not super user", function() {
711+ var controller = makeController();
712+ spyOn($scope, "isAvailableDisabled").and.returnValue(false);
713+ $scope.available = [
714+ {
715+ fstype: null,
716+ $selected: true
717+ }
718+ ];
719+ $scope.isSuperUser = function() { return false; };
720
721 expect($scope.canCreateCacheSet()).toBe(false);
722 });
723@@ -2538,6 +2713,7 @@
724 $selected: true
725 }
726 ];
727+ $scope.isSuperUser = function() { return true; };
728
729 expect($scope.canCreateCacheSet()).toBe(true);
730 });
731@@ -2584,6 +2760,7 @@
732 it("returns false when isAvailableDisabled is true", function() {
733 var controller = makeController();
734 spyOn($scope, "isAvailableDisabled").and.returnValue(true);
735+ $scope.isSuperUser = function() { return true; };
736
737 expect($scope.canCreateBcache()).toBe(false);
738 });
739@@ -2592,6 +2769,7 @@
740 var controller = makeController();
741 spyOn($scope, "isAvailableDisabled").and.returnValue(false);
742 $scope.available = [ { $selected: true }, { $selected: true }];
743+ $scope.isSuperUser = function() { return true; };
744
745 expect($scope.canCreateBcache()).toBe(false);
746 });
747@@ -2606,6 +2784,7 @@
748 }
749 ];
750 $scope.cachesets = [{}];
751+ $scope.isSuperUser = function() { return true; };
752
753 expect($scope.canCreateBcache()).toBe(false);
754 });
755@@ -2621,6 +2800,7 @@
756 }
757 ];
758 $scope.cachesets = [{}];
759+ $scope.isSuperUser = function() { return true; };
760
761 expect($scope.canCreateBcache()).toBe(false);
762 });
763@@ -2636,6 +2816,23 @@
764 }
765 ];
766 $scope.cachesets = [];
767+ $scope.isSuperUser = function() { return true; };
768+
769+ expect($scope.canCreateBcache()).toBe(false);
770+ });
771+
772+ it("returns false if not super user ",
773+ function() {
774+ var controller = makeController();
775+ spyOn($scope, "isAvailableDisabled").and.returnValue(false);
776+ $scope.available = [
777+ {
778+ fstype: null,
779+ $selected: true
780+ }
781+ ];
782+ $scope.cachesets = [{}];
783+ $scope.isSuperUser = function() { return false; };
784
785 expect($scope.canCreateBcache()).toBe(false);
786 });
787@@ -2651,6 +2848,7 @@
788 }
789 ];
790 $scope.cachesets = [{}];
791+ $scope.isSuperUser = function() { return true; };
792
793 expect($scope.canCreateBcache()).toBe(true);
794 });
795@@ -2915,6 +3113,7 @@
796 it("returns false isAvailableDisabled returns true", function() {
797 var controller = makeController();
798 spyOn($scope, "isAvailableDisabled").and.returnValue(true);
799+ $scope.isSuperUser = function() { return true; };
800 expect($scope.canCreateRAID()).toBe(false);
801 });
802
803@@ -2922,6 +3121,7 @@
804 var controller = makeController();
805 spyOn($scope, "isAvailableDisabled").and.returnValue(false);
806 spyOn($scope, "getSelectedAvailable").and.returnValue([{}]);
807+ $scope.isSuperUser = function() { return true; };
808 expect($scope.canCreateRAID()).toBe(false);
809 });
810
811@@ -2930,6 +3130,7 @@
812 spyOn($scope, "isAvailableDisabled").and.returnValue(false);
813 spyOn($scope, "getSelectedAvailable").and.returnValue([{}, {}]);
814 spyOn($scope, "hasUnmountedFilesystem").and.returnValue(true);
815+ $scope.isSuperUser = function() { return true; };
816 expect($scope.canCreateRAID()).toBe(false);
817 });
818
819@@ -2945,6 +3146,16 @@
820 }
821 ]);
822 spyOn($scope, "hasUnmountedFilesystem").and.returnValue(false);
823+ $scope.isSuperUser = function() { return true; };
824+ expect($scope.canCreateRAID()).toBe(false);
825+ });
826+
827+ it("returns false if not super user", function() {
828+ var controller = makeController();
829+ spyOn($scope, "isAvailableDisabled").and.returnValue(false);
830+ spyOn($scope, "getSelectedAvailable").and.returnValue([{}, {}]);
831+ spyOn($scope, "hasUnmountedFilesystem").and.returnValue(false);
832+ $scope.isSuperUser = function() { return false; };
833 expect($scope.canCreateRAID()).toBe(false);
834 });
835
836@@ -2953,6 +3164,8 @@
837 spyOn($scope, "isAvailableDisabled").and.returnValue(false);
838 spyOn($scope, "getSelectedAvailable").and.returnValue([{}, {}]);
839 spyOn($scope, "hasUnmountedFilesystem").and.returnValue(false);
840+ $scope.isSuperUser = function() { return true; };
841+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
842 expect($scope.canCreateRAID()).toBe(true);
843 });
844 });
845@@ -3596,6 +3809,7 @@
846 it("returns false isAvailableDisabled returns true", function() {
847 var controller = makeController();
848 spyOn($scope, "isAvailableDisabled").and.returnValue(true);
849+ $scope.isSuperUser = function() { return true; };
850 expect($scope.canCreateVolumeGroup()).toBe(false);
851 });
852
853@@ -3604,6 +3818,7 @@
854 spyOn($scope, "isAvailableDisabled").and.returnValue(false);
855 spyOn($scope, "getSelectedAvailable").and.returnValue([{}]);
856 spyOn($scope, "hasUnmountedFilesystem").and.returnValue(true);
857+ $scope.isSuperUser = function() { return true; };
858 expect($scope.canCreateVolumeGroup()).toBe(false);
859 });
860
861@@ -3619,6 +3834,16 @@
862 }
863 ]);
864 spyOn($scope, "hasUnmountedFilesystem").and.returnValue(false);
865+ $scope.isSuperUser = function() { return true; };
866+ expect($scope.canCreateVolumeGroup()).toBe(false);
867+ });
868+
869+ it("returns false if not super user", function() {
870+ var controller = makeController();
871+ spyOn($scope, "isAvailableDisabled").and.returnValue(false);
872+ spyOn($scope, "getSelectedAvailable").and.returnValue([{}]);
873+ spyOn($scope, "hasUnmountedFilesystem").and.returnValue(false);
874+ $scope.isSuperUser = function() { return false; };
875 expect($scope.canCreateVolumeGroup()).toBe(false);
876 });
877
878@@ -3627,6 +3852,7 @@
879 spyOn($scope, "isAvailableDisabled").and.returnValue(false);
880 spyOn($scope, "getSelectedAvailable").and.returnValue([{}]);
881 spyOn($scope, "hasUnmountedFilesystem").and.returnValue(false);
882+ $scope.isSuperUser = function() { return true; };
883 expect($scope.canCreateVolumeGroup()).toBe(true);
884 });
885 });
886@@ -4025,8 +4251,28 @@
887 })).toBe(false);
888 });
889
890+ it("returns false when not super user", function() {
891+ var controller = makeController();
892+ $scope.isSuperUser = function() { return false; };
893+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
894+ expect($scope.canEditTags({
895+ type: "physical"
896+ })).toBe(false);
897+ });
898+
899+ it("returns false when isAllStorageDisabled", function() {
900+ var controller = makeController();
901+ $scope.isSuperUser = function() { return true; };
902+ spyOn($scope, "isAllStorageDisabled").and.returnValue(true);
903+ expect($scope.canEditTags({
904+ type: "physical"
905+ })).toBe(false);
906+ });
907+
908 it("returns true for physical", function() {
909 var controller = makeController();
910+ $scope.isSuperUser = function() { return true; };
911+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
912 expect($scope.canEditTags({
913 type: "physical"
914 })).toBe(true);
915@@ -4034,6 +4280,8 @@
916
917 it("returns true for virtual", function() {
918 var controller = makeController();
919+ $scope.isSuperUser = function() { return true; };
920+ spyOn($scope, "isAllStorageDisabled").and.returnValue(false);
921 expect($scope.canEditTags({
922 type: "virtual"
923 })).toBe(true);
924@@ -4095,4 +4343,60 @@
925 expect(disk.tags).toEqual(tags);
926 });
927 });
928+
929+ describe("isAllStorageDisabled", function() {
930+
931+ var RegionConnection, UserManager;
932+ beforeEach(inject(function($injector) {
933+ UsersManager = $injector.get("UsersManager");
934+ RegionConnection = $injector.get("RegionConnection");
935+
936+ // Mock buildSocket so an actual connection is not made.
937+ webSocket = new MockWebSocket();
938+ spyOn(RegionConnection, "buildSocket").and.returnValue(webSocket);
939+ }));
940+
941+ it("false when status is Ready", function() {
942+ var controller = makeController();
943+ $scope.node.status = "Ready";
944+ spyOn(UsersManager, "getAuthUser").and.returnValue(
945+ { is_superuser: true });
946+ expect($scope.isAllStorageDisabled()).toBe(false);
947+ });
948+
949+ it("false when status is Allocated", function() {
950+ var controller = makeController();
951+ $scope.node.status = "Allocated";
952+ spyOn(UsersManager, "getAuthUser").and.returnValue(
953+ { is_superuser: true });
954+ expect($scope.isAllStorageDisabled()).toBe(false);
955+ });
956+
957+ it("false when Allocated and owned", function() {
958+ var controller = makeController();
959+ var user = makeName("user");
960+ $scope.node.status = "Allocated";
961+ $scope.node.owner = user;
962+ spyOn(UsersManager, "getAuthUser").and.returnValue(
963+ { is_superuser: false, username: user });
964+ expect($scope.isAllStorageDisabled()).toBe(false);
965+ });
966+
967+ it("true when not admin", function() {
968+ var controller = makeController();
969+ $scope.node.status = "Allocated";
970+ $scope.node.owner = makeName("user");
971+ spyOn(UsersManager, "getAuthUser").and.returnValue(
972+ { is_superuser: false, username: makeName("user") });
973+ expect($scope.isAllStorageDisabled()).toBe(true);
974+ });
975+
976+ it("true otherwise", function() {
977+ var controller = makeController();
978+ $scope.node.status = makeName("status");
979+ spyOn(UsersManager, "getAuthUser").and.returnValue(
980+ { is_superuser: true });
981+ expect($scope.isAllStorageDisabled()).toBe(true);
982+ });
983+ });
984 });
985
986=== modified file 'src/maasserver/static/partials/node-details.html'
987--- src/maasserver/static/partials/node-details.html 2015-11-01 00:22:00 +0000
988+++ src/maasserver/static/partials/node-details.html 2015-11-03 04:58:42 +0000
989@@ -652,6 +652,16 @@
990 </li>
991 </ul>
992 </div>
993+ <div class="twelve-col error ng-hide" data-ng-show="isAllStorageDisabled()">
994+ <ul class="flash-messages">
995+ <li class="flash-messages__item info ng-hide" data-ng-show="isSuperUser()">
996+ Storage configuration cannot be modified unless the node is Ready or Allocated.
997+ </li>
998+ <li class="flash-messages__item info ng-hide" data-ng-show="!isSuperUser()">
999+ Storage configuration cannot be modified unless the node is Ready or Allocated and you own the node.
1000+ </li>
1001+ </ul>
1002+ </div>
1003 <div class="twelve-col error ng-hide" data-ng-show="!isUbuntuOS()">
1004 <ul class="flash-messages">
1005 <li class="flash-messages__item info">
1006@@ -665,11 +675,13 @@
1007 <header class="table__head">
1008 <div class="table__row">
1009 <div class="table__header table__column--3">
1010- <input class="checkbox" type="checkbox" id="filesystem-check-all"
1011- data-ng-checked="filesystemAllSelected"
1012- data-ng-click="toggleFilesystemAllSelect()"
1013- data-ng-disabled="isFilesystemsDisabled()" />
1014- <label class="checkbox-label" for="filesystem-check-all"></label>
1015+ <span data-ng-hide="isFilesystemsDisabled()">
1016+ <input class="checkbox" type="checkbox" id="filesystem-check-all"
1017+ data-ng-checked="filesystemAllSelected"
1018+ data-ng-click="toggleFilesystemAllSelect()"
1019+ data-ng-hide="isFilesystemsDisabled()" />
1020+ <label class="checkbox-label" for="filesystem-check-all"></label>
1021+ </span>
1022 </div>
1023 <div class="table__header table__column--15">Name</div>
1024 <div class="table__header table__column--27">Size</div>
1025@@ -688,18 +700,20 @@
1026 <div class="table__row"
1027 data-ng-class="{ active: filesystem.$selected }">
1028 <div class="table__data table__column--3">
1029- <input class="checkbox" type="checkbox" id="{$ filesystem.name $}"
1030- data-ng-checked="filesystem.$selected"
1031- data-ng-click="toggleFilesystemSelect(filesystem)"
1032- data-ng-disabled="isFilesystemsDisabled()" />
1033- <label class="checkbox-label" for="{$ filesystem.name $}" ></label>
1034+ <span data-ng-hide="isFilesystemsDisabled()">
1035+ <input class="checkbox" type="checkbox" id="{$ filesystem.name $}"
1036+ data-ng-checked="filesystem.$selected"
1037+ data-ng-click="toggleFilesystemSelect(filesystem)"
1038+ data-ng-disabled="isFilesystemsDisabled()" />
1039+ <label class="checkbox-label" for="{$ filesystem.name $}" ></label>
1040+ </span>
1041 </div>
1042 <div class="table__data table__column--15">{$ filesystem.name $}</div>
1043 <div class="table__data table__column--27">{$ filesystem.size_human $}</div>
1044 <div class="table__data table__column--15">{$ filesystem.fstype $}</div>
1045 <div class="table__data table__column--30">{$ filesystem.mount_point $}</div>
1046 <div class="table__data table__column--10">
1047- <div class="table__controls">
1048+ <div class="table__controls" data-ng-hide="isAllStorageDisabled()">
1049 <a class="icon delete" data-ng-click="quickFilesystemUnmount(filesystem)">Unmount</a>
1050 </div>
1051 </div>
1052@@ -736,7 +750,7 @@
1053 <input class="checkbox" type="checkbox" id="cachesets-check-all"
1054 data-ng-checked="cachesetsAllSelected"
1055 data-ng-click="toggleCacheSetAllSelect()"
1056- data-ng-disabled="isCacheSetsDisabled()" />
1057+ data-ng-hide="isCacheSetsDisabled()" />
1058 <label class="checkbox-label" for="cachesets-check-all"></label>
1059 </div>
1060 <div class="table__header table__column--15">Name</div>
1061@@ -753,7 +767,7 @@
1062 <input class="checkbox" type="checkbox" id="{$ cacheset.name $}"
1063 data-ng-checked="cacheset.$selected"
1064 data-ng-click="toggleCacheSetSelect(cacheset)"
1065- data-ng-disabled="isCacheSetsDisabled()" />
1066+ data-ng-hide="isCacheSetsDisabled()" />
1067 <label class="checkbox-label" for="{$ cacheset.name $}" ></label>
1068 </div>
1069 <div class="table__data table__column--15">{$ cacheset.name $}</div>
1070@@ -797,11 +811,13 @@
1071 <header class="table__head">
1072 <div class="table__row">
1073 <div class="table__header table__column--3">
1074- <input class="checkbox" type="checkbox" id="available-check-all"
1075- data-ng-checked="availableAllSelected"
1076- data-ng-click="toggleAvailableAllSelect()"
1077- data-ng-disabled="isAvailableDisabled()" />
1078- <label class="checkbox-label" for="available-check-all"></label>
1079+ <span data-ng-hide="isAvailableDisabled()">
1080+ <input class="checkbox" type="checkbox" id="available-check-all"
1081+ data-ng-checked="availableAllSelected"
1082+ data-ng-click="toggleAvailableAllSelect()"
1083+ data-ng-disabled="isAvailableDisabled()" />
1084+ <label class="checkbox-label" for="available-check-all"></label>
1085+ </span>
1086 </div>
1087 <div class="table__header table__column--20">
1088 <a data-ng-click="column = 'name'" data-ng-class="{active: column === 'name'}">Name</a>
1089@@ -827,11 +843,13 @@
1090 <div class="table__row"
1091 data-ng-class="{ active: item.$selected }">
1092 <div class="table__data table__column--3">
1093- <input class="checkbox" type="checkbox" id="{$ item.name $}"
1094- data-ng-checked="item.$selected"
1095- data-ng-click="toggleAvailableSelect(item)"
1096- data-ng-disabled="isAvailableDisabled()" />
1097- <label class="checkbox-label" for="{$ item.name $}" ></label>
1098+ <span data-ng-hide="isAvailableDisabled()">
1099+ <input class="checkbox" type="checkbox" id="{$ item.name $}"
1100+ data-ng-checked="item.$selected"
1101+ data-ng-click="toggleAvailableSelect(item)"
1102+ data-ng-disabled="isAvailableDisabled()" />
1103+ <label class="checkbox-label" for="{$ item.name $}" ></label>
1104+ </span>
1105 </div>
1106 <div class="table__data table__column--20" data-ng-show="column === 'name'">
1107 <input type="text" class="table__input"
1108@@ -839,7 +857,7 @@
1109 data-maas-enter-blur
1110 data-ng-blur="saveAvailableName(item)"
1111 data-ng-class="{ invalid: isNameInvalid(item) }"
1112- data-ng-disabled="item.type === 'partition'"
1113+ data-ng-disabled="item.type === 'partition' || isAllStorageDisabled() || !isSuperUser()"
1114 data-ng-change="nameHasChanged(item)">
1115 </div>
1116 <div class="table__data table__column--20 ng-hide" data-ng-show="column === 'model'">{$ item.model $}</div>
1117@@ -1224,15 +1242,19 @@
1118 </section>
1119 <a class="link-cta-ubuntu secondary margin-right"
1120 data-ng-disabled="!canCreateRAID()"
1121+ data-ng-hide="isAllStorageDisabled() || !isSuperUser()"
1122 data-ng-click="createRAID()">Create RAID</a>
1123 <a class="link-cta-ubuntu secondary margin-right"
1124 data-ng-disabled="!canCreateVolumeGroup()"
1125+ data-ng-hide="isAllStorageDisabled() || !isSuperUser()"
1126 data-ng-click="createVolumeGroup()">Create volume group</a>
1127 <a class="link-cta-ubuntu secondary margin-right"
1128 data-ng-disabled="!canCreateCacheSet()"
1129+ data-ng-hide="isAllStorageDisabled() || !isSuperUser()"
1130 data-ng-click="createCacheSet()">Create cache Set</a>
1131 <a class="link-cta-ubuntu secondary"
1132 data-ng-disabled="!canCreateBcache()"
1133+ data-ng-hide="isAllStorageDisabled() || !isSuperUser()"
1134 data-ng-click="createBcache()">Create bcache</a>
1135 </div>
1136 <div class="twelve-col padding-bottom">
1137
1138=== modified file 'src/maasserver/websockets/handlers/node.py'
1139--- src/maasserver/websockets/handlers/node.py 2015-10-30 20:53:53 +0000
1140+++ src/maasserver/websockets/handlers/node.py 2015-11-03 04:58:42 +0000
1141@@ -730,6 +730,12 @@
1142 fstype = params.get('fstype')
1143 mount_point = params.get('mount_point')
1144
1145+ if node.status not in [NODE_STATUS.ALLOCATED, NODE_STATUS.READY]:
1146+ raise HandlerError(
1147+ "Node must be allocated or ready to edit storage")
1148+ if not self.user.is_superuser and node.owner_id != self.user.id:
1149+ raise HandlerPermissionError()
1150+
1151 if partition_id:
1152 self.update_partition_filesystem(
1153 node, block_id, partition_id, fstype, mount_point)
1154
1155=== modified file 'src/maasserver/websockets/handlers/tests/test_node.py'
1156--- src/maasserver/websockets/handlers/tests/test_node.py 2015-10-30 20:50:01 +0000
1157+++ src/maasserver/websockets/handlers/tests/test_node.py 2015-11-03 04:58:42 +0000
1158@@ -1160,7 +1160,10 @@
1159 user = factory.make_admin()
1160 handler = NodeHandler(user, {})
1161 architecture = make_usable_architecture(self)
1162- node = factory.make_Node(interface=True, architecture=architecture)
1163+ node = factory.make_Node(
1164+ interface=True,
1165+ architecture=architecture,
1166+ status=NODE_STATUS.ALLOCATED)
1167 block_device = factory.make_PhysicalBlockDevice(node=node)
1168 fs = factory.make_Filesystem(block_device=block_device)
1169 handler.update_filesystem({
1170@@ -1176,7 +1179,10 @@
1171 user = factory.make_admin()
1172 handler = NodeHandler(user, {})
1173 architecture = make_usable_architecture(self)
1174- node = factory.make_Node(interface=True, architecture=architecture)
1175+ node = factory.make_Node(
1176+ interface=True,
1177+ architecture=architecture,
1178+ status=NODE_STATUS.ALLOCATED)
1179 partition = factory.make_Partition(node=node)
1180 fs = factory.make_Filesystem(partition=partition)
1181 handler.update_filesystem({
1182@@ -1193,7 +1199,10 @@
1183 user = factory.make_admin()
1184 handler = NodeHandler(user, {})
1185 architecture = make_usable_architecture(self)
1186- node = factory.make_Node(interface=True, architecture=architecture)
1187+ node = factory.make_Node(
1188+ interface=True,
1189+ architecture=architecture,
1190+ status=NODE_STATUS.ALLOCATED)
1191 block_device = factory.make_PhysicalBlockDevice(node=node)
1192 fs = factory.make_Filesystem(block_device=block_device)
1193 handler.update_filesystem({
1194@@ -1209,7 +1218,10 @@
1195 user = factory.make_admin()
1196 handler = NodeHandler(user, {})
1197 architecture = make_usable_architecture(self)
1198- node = factory.make_Node(interface=True, architecture=architecture)
1199+ node = factory.make_Node(
1200+ interface=True,
1201+ architecture=architecture,
1202+ status=NODE_STATUS.ALLOCATED)
1203 partition = factory.make_Partition(node=node)
1204 fs = factory.make_Filesystem(partition=partition)
1205 handler.update_filesystem({
1206@@ -1226,7 +1238,10 @@
1207 user = factory.make_admin()
1208 handler = NodeHandler(user, {})
1209 architecture = make_usable_architecture(self)
1210- node = factory.make_Node(interface=True, architecture=architecture)
1211+ node = factory.make_Node(
1212+ interface=True,
1213+ architecture=architecture,
1214+ status=NODE_STATUS.ALLOCATED)
1215 block_device = factory.make_PhysicalBlockDevice(node=node)
1216 fs = factory.make_Filesystem(block_device=block_device)
1217 new_fstype = factory.pick_choice(
1218@@ -1245,7 +1260,10 @@
1219 user = factory.make_admin()
1220 handler = NodeHandler(user, {})
1221 architecture = make_usable_architecture(self)
1222- node = factory.make_Node(interface=True, architecture=architecture)
1223+ node = factory.make_Node(
1224+ interface=True,
1225+ architecture=architecture,
1226+ status=NODE_STATUS.ALLOCATED)
1227 partition = factory.make_Partition(node=node)
1228 fs = factory.make_Filesystem(partition=partition)
1229 new_fstype = factory.pick_choice(
1230@@ -1265,7 +1283,10 @@
1231 user = factory.make_admin()
1232 handler = NodeHandler(user, {})
1233 architecture = make_usable_architecture(self)
1234- node = factory.make_Node(interface=True, architecture=architecture)
1235+ node = factory.make_Node(
1236+ interface=True,
1237+ architecture=architecture,
1238+ status=NODE_STATUS.ALLOCATED)
1239 block_device = factory.make_PhysicalBlockDevice(node=node)
1240 fstype = factory.pick_choice(FILESYSTEM_FORMAT_TYPE_CHOICES)
1241 handler.update_filesystem({
1242@@ -1281,7 +1302,10 @@
1243 user = factory.make_admin()
1244 handler = NodeHandler(user, {})
1245 architecture = make_usable_architecture(self)
1246- node = factory.make_Node(interface=True, architecture=architecture)
1247+ node = factory.make_Node(
1248+ interface=True,
1249+ architecture=architecture,
1250+ status=NODE_STATUS.ALLOCATED)
1251 partition = factory.make_Partition(node=node)
1252 fstype = factory.pick_choice(FILESYSTEM_FORMAT_TYPE_CHOICES)
1253 handler.update_filesystem({
1254@@ -1298,7 +1322,10 @@
1255 user = factory.make_admin()
1256 handler = NodeHandler(user, {})
1257 architecture = make_usable_architecture(self)
1258- node = factory.make_Node(interface=True, architecture=architecture)
1259+ node = factory.make_Node(
1260+ interface=True,
1261+ architecture=architecture,
1262+ status=NODE_STATUS.READY)
1263 block_device = factory.make_PhysicalBlockDevice(node=node)
1264 factory.make_Filesystem(block_device=block_device)
1265 handler.update_filesystem({
1266@@ -1314,7 +1341,10 @@
1267 user = factory.make_admin()
1268 handler = NodeHandler(user, {})
1269 architecture = make_usable_architecture(self)
1270- node = factory.make_Node(interface=True, architecture=architecture)
1271+ node = factory.make_Node(
1272+ interface=True,
1273+ architecture=architecture,
1274+ status=NODE_STATUS.READY)
1275 partition = factory.make_Partition(node=node)
1276 factory.make_Filesystem(partition=partition)
1277 handler.update_filesystem({
1278@@ -1331,7 +1361,10 @@
1279 user = factory.make_admin()
1280 handler = NodeHandler(user, {})
1281 architecture = make_usable_architecture(self)
1282- node = factory.make_Node(interface=True, architecture=architecture)
1283+ node = factory.make_Node(
1284+ interface=True,
1285+ architecture=architecture,
1286+ status=NODE_STATUS.ALLOCATED)
1287 block_device = factory.make_PhysicalBlockDevice(node=node)
1288 new_name = factory.make_name("new")
1289 handler.update_disk({
1290@@ -1345,7 +1378,10 @@
1291 user = factory.make_admin()
1292 handler = NodeHandler(user, {})
1293 architecture = make_usable_architecture(self)
1294- node = factory.make_Node(interface=True, architecture=architecture)
1295+ node = factory.make_Node(
1296+ interface=True,
1297+ architecture=architecture,
1298+ status=NODE_STATUS.ALLOCATED)
1299 block_device = factory.make_VirtualBlockDevice(node=node)
1300 new_name = factory.make_name("new")
1301 handler.update_disk({
1302@@ -1359,7 +1395,10 @@
1303 user = factory.make_admin()
1304 handler = NodeHandler(user, {})
1305 architecture = make_usable_architecture(self)
1306- node = factory.make_Node(interface=True, architecture=architecture)
1307+ node = factory.make_Node(
1308+ interface=True,
1309+ architecture=architecture,
1310+ status=NODE_STATUS.ALLOCATED)
1311 block_device = factory.make_PhysicalBlockDevice(node=node)
1312 handler.delete_disk({
1313 'system_id': node.system_id,
1314@@ -1371,7 +1410,10 @@
1315 user = factory.make_admin()
1316 handler = NodeHandler(user, {})
1317 architecture = make_usable_architecture(self)
1318- node = factory.make_Node(interface=True, architecture=architecture)
1319+ node = factory.make_Node(
1320+ interface=True,
1321+ architecture=architecture,
1322+ status=NODE_STATUS.ALLOCATED)
1323 partition = factory.make_Partition(node=node)
1324 handler.delete_partition({
1325 'system_id': node.system_id,
1326@@ -1383,7 +1425,10 @@
1327 user = factory.make_admin()
1328 handler = NodeHandler(user, {})
1329 architecture = make_usable_architecture(self)
1330- node = factory.make_Node(interface=True, architecture=architecture)
1331+ node = factory.make_Node(
1332+ interface=True,
1333+ architecture=architecture,
1334+ status=NODE_STATUS.ALLOCATED)
1335 volume_group = factory.make_FilesystemGroup(
1336 node=node, group_type=FILESYSTEM_GROUP_TYPE.LVM_VG)
1337 handler.delete_volume_group({
1338@@ -1396,7 +1441,10 @@
1339 user = factory.make_admin()
1340 handler = NodeHandler(user, {})
1341 architecture = make_usable_architecture(self)
1342- node = factory.make_Node(interface=True, architecture=architecture)
1343+ node = factory.make_Node(
1344+ interface=True,
1345+ architecture=architecture,
1346+ status=NODE_STATUS.ALLOCATED)
1347 cache_set = factory.make_CacheSet(node=node)
1348 handler.delete_cache_set({
1349 'system_id': node.system_id,
1350@@ -1408,7 +1456,10 @@
1351 user = factory.make_admin()
1352 handler = NodeHandler(user, {})
1353 architecture = make_usable_architecture(self)
1354- node = factory.make_Node(interface=True, architecture=architecture)
1355+ node = factory.make_Node(
1356+ interface=True,
1357+ architecture=architecture,
1358+ status=NODE_STATUS.ALLOCATED)
1359 partition_table = factory.make_PartitionTable(node=node)
1360 size = partition_table.block_device.size / 2
1361 handler.create_partition({