Merge lp:~ltrager/maas/storage_editable_only_when_ready_or_allocated into lp:~maas-committers/maas/trunk
- storage_editable_only_when_ready_or_allocated
- Merge into trunk
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 |
Related bugs: |
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.
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.
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.
Lee Trager (ltrager) wrote : | # |
I've updated the branch fixing the issues you mentioned. Let me know if you see anything else.
Blake Rouse (blake-rouse) wrote : | # |
Looks good. Thanks for making all the fixes!
Preview Diff
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({ |
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.)