Merge ~ya-bo-ng/maas:esxi-rebuild-2 into maas:master
- Git
- lp:~ya-bo-ng/maas
- esxi-rebuild-2
- Merge into master
Status: | Merged |
---|---|
Approved by: | Anthony Dillon |
Approved revision: | 86b71e599f8ab1df011479426171e636d58ab6c9 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~ya-bo-ng/maas:esxi-rebuild-2 |
Merge into: | maas:master |
Diff against target: |
2258 lines (+1397/-303) 14 files modified
src/maasserver/static/js/angular/controllers/node_details_storage.js (+257/-8) src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js (+310/-8) src/maasserver/static/js/angular/directives/nodedetails/storage_datastores.js (+9/-0) src/maasserver/static/js/angular/entry.js (+5/-1) src/maasserver/static/js/angular/factories/machines.js (+15/-0) src/maasserver/static/js/angular/factories/tests/test_machines.js (+52/-0) src/maasserver/static/partials/node-details.html (+256/-157) src/maasserver/static/partials/nodedetails/storage/datastores.html (+112/-0) src/maasserver/static/partials/nodedetails/storage/disks-partitions.html (+319/-121) src/maasserver/static/partials/nodedetails/storage/filesystems.html (+1/-1) src/maasserver/static/scss/_base_tables.scss (+0/-1) src/maasserver/static/scss/_patterns_notification.scss (+12/-0) src/maasserver/static/scss/_tables.scss (+45/-6) src/maasserver/static/scss/_utils.scss (+4/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Lilyana Videnova (community) | Approve | ||
Steve Rydz (community) | Approve | ||
Review via email: mp+367016@code.launchpad.net |
Commit message
Update machine storage to add datastore support for ESXi
Description of the change
## Done
Updated the machine storage UI to add the ability to create a ESXi datastore from available disk.
## QA
- Open a machine is Ready state
- Open the storage tab
- Change the layout to ESXi and confirm
- See that there is a datastore table.
- Under available disks table, select a disk and create a datastore
- See that is works
- Change the layout and confirm
- Check it does not show the datastores table
## Sreenshots
Storage UI: https:/
Layout change confirmation: https:/
ESXi layout: https:/
Creating a datastore: https:/
Lilyana Videnova (lilyanavidenova) wrote : | # |
LGTM :)
Lilyana Videnova (lilyanavidenova) : | # |
Blake Rouse (blake-rouse) wrote : | # |
JS needs fixing.
Anthony Dillon (ya-bo-ng) wrote : | # |
All done Blake, thanks
Blake Rouse (blake-rouse) wrote : | # |
Approved.
By the way.... smaller branches next time!!!!!
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b esxi-rebuild-2 lp:~ya-bo-ng/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b esxi-rebuild-2 lp:~ya-bo-ng/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b esxi-rebuild-2 lp:~ya-bo-ng/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
Preview Diff
1 | diff --git a/src/maasserver/static/js/angular/controllers/node_details_storage.js b/src/maasserver/static/js/angular/controllers/node_details_storage.js | |||
2 | index 2ddf2e0..87edee0 100644 | |||
3 | --- a/src/maasserver/static/js/angular/controllers/node_details_storage.js | |||
4 | +++ b/src/maasserver/static/js/angular/controllers/node_details_storage.js | |||
5 | @@ -45,12 +45,23 @@ export function removeAvailableByNew() { | |||
6 | 45 | }; | 45 | }; |
7 | 46 | } | 46 | } |
8 | 47 | 47 | ||
9 | 48 | export function datastoresOnly() { | ||
10 | 49 | return function(filesystems) { | ||
11 | 50 | return filesystems.filter(filesystem => { | ||
12 | 51 | return filesystem.used_for === "VMFS Datastore"; | ||
13 | 52 | }); | ||
14 | 53 | }; | ||
15 | 54 | } | ||
16 | 55 | |||
17 | 48 | /* @ngInject */ | 56 | /* @ngInject */ |
18 | 49 | export function NodeStorageController( | 57 | export function NodeStorageController( |
19 | 50 | $scope, | 58 | $scope, |
20 | 51 | MachinesManager, | 59 | MachinesManager, |
21 | 52 | ConverterService, | 60 | ConverterService, |
23 | 53 | UsersManager | 61 | UsersManager, |
24 | 62 | $log, | ||
25 | 63 | $timeout, | ||
26 | 64 | $filter | ||
27 | 54 | ) { | 65 | ) { |
28 | 55 | // From models/partitiontable.py - must be kept in sync. | 66 | // From models/partitiontable.py - must be kept in sync. |
29 | 56 | var INITIAL_PARTITION_OFFSET = 4 * 1024 * 1024; | 67 | var INITIAL_PARTITION_OFFSET = 4 * 1024 * 1024; |
30 | @@ -129,6 +140,8 @@ export function NodeStorageController( | |||
31 | 129 | } | 140 | } |
32 | 130 | ]; | 141 | ]; |
33 | 131 | 142 | ||
34 | 143 | var datastoreOnly = $filter("datastoresOnly"); | ||
35 | 144 | |||
36 | 132 | $scope.tableInfo = { column: "name" }; | 145 | $scope.tableInfo = { column: "name" }; |
37 | 133 | $scope.has_disks = false; | 146 | $scope.has_disks = false; |
38 | 134 | $scope.filesystems = []; | 147 | $scope.filesystems = []; |
39 | @@ -148,6 +161,235 @@ export function NodeStorageController( | |||
40 | 148 | $scope.nodeManager = MachinesManager; | 161 | $scope.nodeManager = MachinesManager; |
41 | 149 | $scope.used = []; | 162 | $scope.used = []; |
42 | 150 | $scope.showMembers = []; | 163 | $scope.showMembers = []; |
43 | 164 | $scope.createNewDatastore = false; | ||
44 | 165 | $scope.addToExistingDatastore = false; | ||
45 | 166 | $scope.datastores = { | ||
46 | 167 | new: {}, | ||
47 | 168 | old: {} | ||
48 | 169 | }; | ||
49 | 170 | $scope.selectedAvailableDatastores = []; | ||
50 | 171 | $scope.creatingDatastore = false; | ||
51 | 172 | $scope.updatingDatastore = false; | ||
52 | 173 | $scope.updatingOSFamily = false; | ||
53 | 174 | $scope.updatingStorageLayout = false; | ||
54 | 175 | $scope.confirmStorageLayout = false; | ||
55 | 176 | $scope.newLayout = ""; | ||
56 | 177 | $scope.addToDatastoreValid = false; | ||
57 | 178 | |||
58 | 179 | // XXX: Steve Rydz 09/08/2019 | ||
59 | 180 | // Hardcoded for now in current cycle as no mapping exists | ||
60 | 181 | $scope.osFamilies = [ | ||
61 | 182 | { | ||
62 | 183 | id: "linux", | ||
63 | 184 | name: "Linux", | ||
64 | 185 | layouts: [ | ||
65 | 186 | { | ||
66 | 187 | id: "flat", | ||
67 | 188 | name: "Flat" | ||
68 | 189 | }, | ||
69 | 190 | { | ||
70 | 191 | id: "lvm", | ||
71 | 192 | name: "LVM" | ||
72 | 193 | }, | ||
73 | 194 | { | ||
74 | 195 | id: "bcache", | ||
75 | 196 | name: "bcache" | ||
76 | 197 | }, | ||
77 | 198 | { | ||
78 | 199 | id: "vmfs6", | ||
79 | 200 | name: "VMFS6 (VMware ESXI)" | ||
80 | 201 | }, | ||
81 | 202 | { | ||
82 | 203 | id: "blank", | ||
83 | 204 | name: "No storage (blank) layout" | ||
84 | 205 | } | ||
85 | 206 | ] | ||
86 | 207 | } | ||
87 | 208 | ]; | ||
88 | 209 | |||
89 | 210 | $scope.osFamily = $scope.osFamilies[0]; | ||
90 | 211 | $scope.storageLayout = $scope.osFamily.layouts.find(layout => { | ||
91 | 212 | return layout.id === $scope.node.detected_storage_layout; | ||
92 | 213 | }); | ||
93 | 214 | |||
94 | 215 | $scope.openStorageLayoutConfirm = function(selectedLayout) { | ||
95 | 216 | $scope.osFamily.layouts.forEach(layout => { | ||
96 | 217 | if (layout.id === selectedLayout) { | ||
97 | 218 | $scope.newLayout = layout; | ||
98 | 219 | } | ||
99 | 220 | }); | ||
100 | 221 | $scope.confirmStorageLayout = true; | ||
101 | 222 | }; | ||
102 | 223 | |||
103 | 224 | $scope.closeStorageLayoutConfirm = function() { | ||
104 | 225 | $scope.confirmStorageLayout = false; | ||
105 | 226 | }; | ||
106 | 227 | |||
107 | 228 | $scope.updateStorageLayout = function(storageLayout) { | ||
108 | 229 | storageLayout = $scope.storageLayout = $scope.newLayout; | ||
109 | 230 | |||
110 | 231 | var params = { | ||
111 | 232 | system_id: $scope.node.system_id, | ||
112 | 233 | storage_layout: storageLayout.id | ||
113 | 234 | }; | ||
114 | 235 | |||
115 | 236 | $scope.updatingStorageLayout = true; | ||
116 | 237 | |||
117 | 238 | MachinesManager.applyStorageLayout(params) | ||
118 | 239 | .then(function() { | ||
119 | 240 | $timeout(function() { | ||
120 | 241 | $scope.updatingStorageLayout = false; | ||
121 | 242 | }, 0); | ||
122 | 243 | }) | ||
123 | 244 | .catch(function(error) { | ||
124 | 245 | $log.error(error); | ||
125 | 246 | $timeout(function() { | ||
126 | 247 | $scope.updatingStorageLayout = false; | ||
127 | 248 | }, 0); | ||
128 | 249 | }); | ||
129 | 250 | |||
130 | 251 | $scope.closeStorageLayoutConfirm(); | ||
131 | 252 | }; | ||
132 | 253 | |||
133 | 254 | $scope.openNewDatastorePanel = function() { | ||
134 | 255 | $scope.createNewDatastore = true; | ||
135 | 256 | var selectedDisks = $scope.getSelectedAvailable(); | ||
136 | 257 | $scope.datastores.new = { | ||
137 | 258 | id: selectedDisks[0].id, | ||
138 | 259 | name: "", | ||
139 | 260 | mountpoint: selectedDisks[0].mount_point, | ||
140 | 261 | filesystem: "VMFS6", | ||
141 | 262 | size: selectedDisks[0].size_human | ||
142 | 263 | }; | ||
143 | 264 | }; | ||
144 | 265 | |||
145 | 266 | $scope.closeNewDatastorePanel = function() { | ||
146 | 267 | $scope.createNewDatastore = false; | ||
147 | 268 | $scope.datastores.new = {}; | ||
148 | 269 | }; | ||
149 | 270 | |||
150 | 271 | $scope.openAddToExistingDatastorePanel = function() { | ||
151 | 272 | $scope.addToExistingDatastore = true; | ||
152 | 273 | $scope.selectedAvailableDatastores = $scope.getSelectedAvailable(); | ||
153 | 274 | $scope.datastores.old = datastoreOnly($scope.node.disks)[0]; | ||
154 | 275 | }; | ||
155 | 276 | |||
156 | 277 | $scope.closeAddToExistingDatastorePanel = function() { | ||
157 | 278 | $scope.addToExistingDatastore = false; | ||
158 | 279 | $scope.datastores.new = {}; | ||
159 | 280 | }; | ||
160 | 281 | |||
161 | 282 | $scope.canPerformActionOnDatastoreSet = function() { | ||
162 | 283 | var editing = $scope.addToExistingDatastore || $scope.createNewDatastore; | ||
163 | 284 | var selected = $scope.selectedAvailableDatastores.length > 0; | ||
164 | 285 | var vmfs6 = $scope.storageLayout.id === "vmfs6"; | ||
165 | 286 | return !editing && selected && vmfs6; | ||
166 | 287 | }; | ||
167 | 288 | |||
168 | 289 | $scope.createDatastore = function() { | ||
169 | 290 | $scope.createNewDatastore = true; | ||
170 | 291 | |||
171 | 292 | var selectedAvailable = $scope.getSelectedAvailable(); | ||
172 | 293 | var blockDeviceIDs = []; | ||
173 | 294 | var partitionIDs = []; | ||
174 | 295 | |||
175 | 296 | selectedAvailable.forEach(function(item) { | ||
176 | 297 | if (item.type === "partition") { | ||
177 | 298 | partitionIDs.push(item.partition_id); | ||
178 | 299 | } else { | ||
179 | 300 | blockDeviceIDs.push(item.block_id); | ||
180 | 301 | } | ||
181 | 302 | }); | ||
182 | 303 | |||
183 | 304 | var params = { | ||
184 | 305 | system_id: $scope.node.system_id, | ||
185 | 306 | block_devices: blockDeviceIDs, | ||
186 | 307 | partitions: partitionIDs, | ||
187 | 308 | name: $scope.datastores.new.name | ||
188 | 309 | }; | ||
189 | 310 | |||
190 | 311 | $scope.creatingDatastore = true; | ||
191 | 312 | |||
192 | 313 | MachinesManager.createDatastore(params) | ||
193 | 314 | .then(function() { | ||
194 | 315 | $timeout(function() { | ||
195 | 316 | $scope.creatingDatastore = false; | ||
196 | 317 | }, 0); | ||
197 | 318 | $scope.closeNewDatastorePanel(); | ||
198 | 319 | $scope.selectedAvailableDatastores = []; | ||
199 | 320 | }) | ||
200 | 321 | .catch(function(error) { | ||
201 | 322 | $log.error(error); | ||
202 | 323 | $timeout(function() { | ||
203 | 324 | $scope.creatingDatastore = false; | ||
204 | 325 | }, 0); | ||
205 | 326 | }); | ||
206 | 327 | }; | ||
207 | 328 | |||
208 | 329 | $scope.checkAddToDatastoreValid = function() { | ||
209 | 330 | var selectedAvailable = $scope.getSelectedAvailable(); | ||
210 | 331 | var valid = true; | ||
211 | 332 | if (selectedAvailable.length < 1) { | ||
212 | 333 | valid = false; | ||
213 | 334 | } | ||
214 | 335 | selectedAvailable.forEach(function(item) { | ||
215 | 336 | if (item.has_partitions) { | ||
216 | 337 | valid = false; | ||
217 | 338 | } | ||
218 | 339 | }); | ||
219 | 340 | $scope.addToDatastoreValid = valid; | ||
220 | 341 | }; | ||
221 | 342 | |||
222 | 343 | $scope.addToDatastore = function() { | ||
223 | 344 | var selectedAvailable = $scope.getSelectedAvailable(); | ||
224 | 345 | var blockDeviceIDs = []; | ||
225 | 346 | var partitionIDs = []; | ||
226 | 347 | |||
227 | 348 | selectedAvailable.forEach(function(item) { | ||
228 | 349 | if (item.type === "partition") { | ||
229 | 350 | partitionIDs.push(item.partition_id); | ||
230 | 351 | } else { | ||
231 | 352 | blockDeviceIDs.push(item.block_id); | ||
232 | 353 | } | ||
233 | 354 | }); | ||
234 | 355 | |||
235 | 356 | var params = { | ||
236 | 357 | system_id: $scope.node.system_id, | ||
237 | 358 | add_block_devices: blockDeviceIDs, | ||
238 | 359 | add_partitions: partitionIDs, | ||
239 | 360 | name: $scope.datastores.old.name, | ||
240 | 361 | vmfs_datastore_id: $scope.datastores.old.id | ||
241 | 362 | }; | ||
242 | 363 | |||
243 | 364 | $scope.updatingDatastore = true; | ||
244 | 365 | |||
245 | 366 | MachinesManager.updateDatastore(params) | ||
246 | 367 | .then(function() { | ||
247 | 368 | $timeout(function() { | ||
248 | 369 | $scope.updatingDatastore = false; | ||
249 | 370 | }, 0); | ||
250 | 371 | $scope.closeAddToExistingDatastorePanel(); | ||
251 | 372 | $scope.selectedAvailableDatastores = []; | ||
252 | 373 | }) | ||
253 | 374 | .catch(function(error) { | ||
254 | 375 | $log.error(error); | ||
255 | 376 | $timeout(function() { | ||
256 | 377 | $scope.updatingDatastore = false; | ||
257 | 378 | }, 0); | ||
258 | 379 | }); | ||
259 | 380 | }; | ||
260 | 381 | |||
261 | 382 | $scope.storageLayoutIsReadOnly = function(layouts) { | ||
262 | 383 | return layouts.length <= 1; | ||
263 | 384 | }; | ||
264 | 385 | |||
265 | 386 | $scope.storageLayoutIsDisabled = function(layouts) { | ||
266 | 387 | return !layouts.length; | ||
267 | 388 | }; | ||
268 | 389 | |||
269 | 390 | $scope.hasStorageLayout = function(storageLayout) { | ||
270 | 391 | return storageLayout ? true : false; | ||
271 | 392 | }; | ||
272 | 151 | 393 | ||
273 | 152 | // Return True if the filesystem is mounted. | 394 | // Return True if the filesystem is mounted. |
274 | 153 | function isMountedFilesystem(filesystem) { | 395 | function isMountedFilesystem(filesystem) { |
275 | @@ -461,6 +703,7 @@ export function NodeStorageController( | |||
276 | 461 | type: disk.type, | 703 | type: disk.type, |
277 | 462 | model: disk.model, | 704 | model: disk.model, |
278 | 463 | serial: disk.serial, | 705 | serial: disk.serial, |
279 | 706 | size_human: disk.size_human, | ||
280 | 464 | tags: getTags(disk), | 707 | tags: getTags(disk), |
281 | 465 | used_for: disk.used_for, | 708 | used_for: disk.used_for, |
282 | 466 | is_boot: disk.is_boot, | 709 | is_boot: disk.is_boot, |
283 | @@ -480,6 +723,7 @@ export function NodeStorageController( | |||
284 | 480 | type: "partition", | 723 | type: "partition", |
285 | 481 | model: "", | 724 | model: "", |
286 | 482 | serial: "", | 725 | serial: "", |
287 | 726 | size_human: partition.size_human, | ||
288 | 483 | tags: [], | 727 | tags: [], |
289 | 484 | used_for: partition.used_for, | 728 | used_for: partition.used_for, |
290 | 485 | is_boot: false | 729 | is_boot: false |
291 | @@ -776,6 +1020,9 @@ export function NodeStorageController( | |||
292 | 776 | } else if (filesystem.original_type === "partition") { | 1020 | } else if (filesystem.original_type === "partition") { |
293 | 777 | // Delete the partition. | 1021 | // Delete the partition. |
294 | 778 | MachinesManager.deletePartition($scope.node, filesystem.original.id); | 1022 | MachinesManager.deletePartition($scope.node, filesystem.original.id); |
295 | 1023 | } else if (filesystem.parent_type === "vmfs6") { | ||
296 | 1024 | // Delete the datastore. | ||
297 | 1025 | MachinesManager.deleteDisk($scope.node, filesystem.id); | ||
298 | 779 | } else { | 1026 | } else { |
299 | 780 | // Delete the disk. | 1027 | // Delete the disk. |
300 | 781 | MachinesManager.deleteFilesystem( | 1028 | MachinesManager.deleteFilesystem( |
301 | @@ -878,6 +1125,8 @@ export function NodeStorageController( | |||
302 | 878 | $scope.toggleAvailableSelect = function(disk) { | 1125 | $scope.toggleAvailableSelect = function(disk) { |
303 | 879 | disk.$selected = !disk.$selected; | 1126 | disk.$selected = !disk.$selected; |
304 | 880 | $scope.updateAvailableSelection(true); | 1127 | $scope.updateAvailableSelection(true); |
305 | 1128 | $scope.selectedAvailableDatastores = $scope.getSelectedAvailable(); | ||
306 | 1129 | $scope.checkAddToDatastoreValid(); | ||
307 | 881 | }; | 1130 | }; |
308 | 882 | 1131 | ||
309 | 883 | // Toggle the selection of all available disks. | 1132 | // Toggle the selection of all available disks. |
310 | @@ -1592,24 +1841,24 @@ export function NodeStorageController( | |||
311 | 1592 | }; | 1841 | }; |
312 | 1593 | 1842 | ||
313 | 1594 | // Return true when the name of the new disk is invalid. | 1843 | // Return true when the name of the new disk is invalid. |
315 | 1595 | $scope.isNewDiskNameInvalid = function() { | 1844 | $scope.isNewDiskNameInvalid = function(newDiskName) { |
316 | 1596 | if (!angular.isObject($scope.node) || !angular.isArray($scope.node.disks)) { | 1845 | if (!angular.isObject($scope.node) || !angular.isArray($scope.node.disks)) { |
317 | 1597 | return true; | 1846 | return true; |
318 | 1598 | } | 1847 | } |
319 | 1599 | 1848 | ||
321 | 1600 | if ($scope.availableNew.name === "") { | 1849 | if (newDiskName === "") { |
322 | 1601 | return true; | 1850 | return true; |
323 | 1602 | } else { | 1851 | } else { |
324 | 1603 | var i, j; | 1852 | var i, j; |
325 | 1604 | for (i = 0; i < $scope.node.disks.length; i++) { | 1853 | for (i = 0; i < $scope.node.disks.length; i++) { |
326 | 1605 | var disk = $scope.node.disks[i]; | 1854 | var disk = $scope.node.disks[i]; |
328 | 1606 | if ($scope.availableNew.name === disk.name) { | 1855 | if (newDiskName === disk.name) { |
329 | 1607 | return true; | 1856 | return true; |
330 | 1608 | } | 1857 | } |
331 | 1609 | if (angular.isArray(disk.partitions)) { | 1858 | if (angular.isArray(disk.partitions)) { |
332 | 1610 | for (j = 0; j < disk.partitions.length; j++) { | 1859 | for (j = 0; j < disk.partitions.length; j++) { |
333 | 1611 | var partition = disk.partitions[j]; | 1860 | var partition = disk.partitions[j]; |
335 | 1612 | if ($scope.availableNew.name === partition.name) { | 1861 | if (newDiskName === partition.name) { |
336 | 1613 | return true; | 1862 | return true; |
337 | 1614 | } | 1863 | } |
338 | 1615 | } | 1864 | } |
339 | @@ -1622,7 +1871,7 @@ export function NodeStorageController( | |||
340 | 1622 | // Return true if bcache can be saved. | 1871 | // Return true if bcache can be saved. |
341 | 1623 | $scope.createBcacheCanSave = function() { | 1872 | $scope.createBcacheCanSave = function() { |
342 | 1624 | return ( | 1873 | return ( |
344 | 1625 | !$scope.isNewDiskNameInvalid() && | 1874 | !$scope.isNewDiskNameInvalid($scope.availableNew.name) && |
345 | 1626 | !$scope.isMountPointInvalid($scope.availableNew.mountPoint) | 1875 | !$scope.isMountPointInvalid($scope.availableNew.mountPoint) |
346 | 1627 | ); | 1876 | ); |
347 | 1628 | }; | 1877 | }; |
348 | @@ -1831,7 +2080,7 @@ export function NodeStorageController( | |||
349 | 1831 | // Return true if RAID can be saved. | 2080 | // Return true if RAID can be saved. |
350 | 1832 | $scope.createRAIDCanSave = function() { | 2081 | $scope.createRAIDCanSave = function() { |
351 | 1833 | return ( | 2082 | return ( |
353 | 1834 | !$scope.isNewDiskNameInvalid() && | 2083 | !$scope.isNewDiskNameInvalid($scope.availableNew.name) && |
354 | 1835 | !$scope.isMountPointInvalid($scope.availableNew.mountPoint) | 2084 | !$scope.isMountPointInvalid($scope.availableNew.mountPoint) |
355 | 1836 | ); | 2085 | ); |
356 | 1837 | }; | 2086 | }; |
357 | @@ -1944,7 +2193,7 @@ export function NodeStorageController( | |||
358 | 1944 | 2193 | ||
359 | 1945 | // Return true if volume group can be saved. | 2194 | // Return true if volume group can be saved. |
360 | 1946 | $scope.createVolumeGroupCanSave = function() { | 2195 | $scope.createVolumeGroupCanSave = function() { |
362 | 1947 | return !$scope.isNewDiskNameInvalid(); | 2196 | return !$scope.isNewDiskNameInvalid($scope.availableNew.name); |
363 | 1948 | }; | 2197 | }; |
364 | 1949 | 2198 | ||
365 | 1950 | // Confirm and create the volume group device. | 2199 | // Confirm and create the volume group device. |
366 | diff --git a/src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js b/src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js | |||
367 | index f267fa8..023c970 100644 | |||
368 | --- a/src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js | |||
369 | +++ b/src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js | |||
370 | @@ -431,6 +431,7 @@ describe("NodeStorageController", function() { | |||
371 | 431 | type: disks[2].type, | 431 | type: disks[2].type, |
372 | 432 | model: disks[2].model, | 432 | model: disks[2].model, |
373 | 433 | serial: disks[2].serial, | 433 | serial: disks[2].serial, |
374 | 434 | size_human: disks[2].size_human, | ||
375 | 434 | tags: disks[2].tags, | 435 | tags: disks[2].tags, |
376 | 435 | used_for: disks[2].used_for, | 436 | used_for: disks[2].used_for, |
377 | 436 | has_partitions: false, | 437 | has_partitions: false, |
378 | @@ -443,6 +444,7 @@ describe("NodeStorageController", function() { | |||
379 | 443 | type: disks[3].type, | 444 | type: disks[3].type, |
380 | 444 | model: disks[3].model, | 445 | model: disks[3].model, |
381 | 445 | serial: disks[3].serial, | 446 | serial: disks[3].serial, |
382 | 447 | size_human: disks[3].size_human, | ||
383 | 446 | tags: disks[3].tags, | 448 | tags: disks[3].tags, |
384 | 447 | used_for: disks[3].used_for, | 449 | used_for: disks[3].used_for, |
385 | 448 | has_partitions: true, | 450 | has_partitions: true, |
386 | @@ -455,6 +457,7 @@ describe("NodeStorageController", function() { | |||
387 | 455 | type: "partition", | 457 | type: "partition", |
388 | 456 | model: "", | 458 | model: "", |
389 | 457 | serial: "", | 459 | serial: "", |
390 | 460 | size_human: disks[3].partitions[1].size_human, | ||
391 | 458 | tags: [], | 461 | tags: [], |
392 | 459 | used_for: disks[3].partitions[1].used_for | 462 | used_for: disks[3].partitions[1].used_for |
393 | 460 | } | 463 | } |
394 | @@ -3597,9 +3600,8 @@ describe("NodeStorageController", function() { | |||
395 | 3597 | it("returns true if blank name", function() { | 3600 | it("returns true if blank name", function() { |
396 | 3598 | makeController(); | 3601 | makeController(); |
397 | 3599 | $scope.node.disks = []; | 3602 | $scope.node.disks = []; |
398 | 3600 | $scope.availableNew.name = ""; | ||
399 | 3601 | 3603 | ||
401 | 3602 | expect($scope.isNewDiskNameInvalid()).toBe(true); | 3604 | expect($scope.isNewDiskNameInvalid("")).toBe(true); |
402 | 3603 | }); | 3605 | }); |
403 | 3604 | 3606 | ||
404 | 3605 | it("returns true if name used by disk", function() { | 3607 | it("returns true if name used by disk", function() { |
405 | @@ -3610,9 +3612,8 @@ describe("NodeStorageController", function() { | |||
406 | 3610 | name: name | 3612 | name: name |
407 | 3611 | } | 3613 | } |
408 | 3612 | ]; | 3614 | ]; |
409 | 3613 | $scope.availableNew.name = name; | ||
410 | 3614 | 3615 | ||
412 | 3615 | expect($scope.isNewDiskNameInvalid()).toBe(true); | 3616 | expect($scope.isNewDiskNameInvalid(name)).toBe(true); |
413 | 3616 | }); | 3617 | }); |
414 | 3617 | 3618 | ||
415 | 3618 | it("returns true if name used by partition", function() { | 3619 | it("returns true if name used by partition", function() { |
416 | @@ -3628,9 +3629,8 @@ describe("NodeStorageController", function() { | |||
417 | 3628 | ] | 3629 | ] |
418 | 3629 | } | 3630 | } |
419 | 3630 | ]; | 3631 | ]; |
420 | 3631 | $scope.availableNew.name = name; | ||
421 | 3632 | 3632 | ||
423 | 3633 | expect($scope.isNewDiskNameInvalid()).toBe(true); | 3633 | expect($scope.isNewDiskNameInvalid(name)).toBe(true); |
424 | 3634 | }); | 3634 | }); |
425 | 3635 | 3635 | ||
426 | 3636 | it("returns false if the name is not already used", function() { | 3636 | it("returns false if the name is not already used", function() { |
427 | @@ -3646,9 +3646,8 @@ describe("NodeStorageController", function() { | |||
428 | 3646 | ] | 3646 | ] |
429 | 3647 | } | 3647 | } |
430 | 3648 | ]; | 3648 | ]; |
431 | 3649 | $scope.availableNew.name = name; | ||
432 | 3650 | 3649 | ||
434 | 3651 | expect($scope.isNewDiskNameInvalid()).toBe(false); | 3650 | expect($scope.isNewDiskNameInvalid(name)).toBe(false); |
435 | 3652 | }); | 3651 | }); |
436 | 3653 | }); | 3652 | }); |
437 | 3654 | 3653 | ||
438 | @@ -5073,4 +5072,307 @@ describe("NodeStorageController", function() { | |||
439 | 5073 | expect($scope.hasStorageLayoutIssues()).toBe(false); | 5072 | expect($scope.hasStorageLayoutIssues()).toBe(false); |
440 | 5074 | }); | 5073 | }); |
441 | 5075 | }); | 5074 | }); |
442 | 5075 | |||
443 | 5076 | describe("openStorageLayoutConfirm", function() { | ||
444 | 5077 | it("sets 'confirmStorageLayout' to true", function() { | ||
445 | 5078 | makeController(); | ||
446 | 5079 | $scope.confirmStorageLayout = false; | ||
447 | 5080 | $scope.osFamilies = [ | ||
448 | 5081 | { | ||
449 | 5082 | id: "linux", | ||
450 | 5083 | name: "Linux", | ||
451 | 5084 | layouts: [ | ||
452 | 5085 | { | ||
453 | 5086 | id: "flat", | ||
454 | 5087 | name: "Flat" | ||
455 | 5088 | }, | ||
456 | 5089 | { | ||
457 | 5090 | id: "lvm", | ||
458 | 5091 | name: "LVM" | ||
459 | 5092 | }, | ||
460 | 5093 | { | ||
461 | 5094 | id: "bcache", | ||
462 | 5095 | name: "bcache" | ||
463 | 5096 | }, | ||
464 | 5097 | { | ||
465 | 5098 | id: "vmfs6", | ||
466 | 5099 | name: "VMFS6 (VMware ESXI)" | ||
467 | 5100 | }, | ||
468 | 5101 | { | ||
469 | 5102 | id: "blank", | ||
470 | 5103 | name: "No storage (blank) layout" | ||
471 | 5104 | } | ||
472 | 5105 | ] | ||
473 | 5106 | } | ||
474 | 5107 | ]; | ||
475 | 5108 | $scope.openStorageLayoutConfirm("flat"); | ||
476 | 5109 | expect($scope.confirmStorageLayout).toBe(true); | ||
477 | 5110 | }); | ||
478 | 5111 | |||
479 | 5112 | it("sets 'newLayout' to layout argument", function() { | ||
480 | 5113 | makeController(); | ||
481 | 5114 | $scope.osFamilies = [ | ||
482 | 5115 | { | ||
483 | 5116 | id: "linux", | ||
484 | 5117 | name: "Linux", | ||
485 | 5118 | layouts: [ | ||
486 | 5119 | { | ||
487 | 5120 | id: "flat", | ||
488 | 5121 | name: "Flat" | ||
489 | 5122 | }, | ||
490 | 5123 | { | ||
491 | 5124 | id: "lvm", | ||
492 | 5125 | name: "LVM" | ||
493 | 5126 | }, | ||
494 | 5127 | { | ||
495 | 5128 | id: "bcache", | ||
496 | 5129 | name: "bcache" | ||
497 | 5130 | }, | ||
498 | 5131 | { | ||
499 | 5132 | id: "vmfs6", | ||
500 | 5133 | name: "VMFS6 (VMware ESXI)" | ||
501 | 5134 | }, | ||
502 | 5135 | { | ||
503 | 5136 | id: "blank", | ||
504 | 5137 | name: "No storage (blank) layout" | ||
505 | 5138 | } | ||
506 | 5139 | ] | ||
507 | 5140 | } | ||
508 | 5141 | ]; | ||
509 | 5142 | $scope.openStorageLayoutConfirm("flat"); | ||
510 | 5143 | expect($scope.newLayout).toEqual($scope.osFamilies[0].layouts[0]); | ||
511 | 5144 | }); | ||
512 | 5145 | }); | ||
513 | 5146 | |||
514 | 5147 | describe("closeStorageLayoutConfirm", function() { | ||
515 | 5148 | it("sets 'confirmStorageLayout' to false", function() { | ||
516 | 5149 | makeController(); | ||
517 | 5150 | $scope.confirmStorageLayout = true; | ||
518 | 5151 | $scope.closeStorageLayoutConfirm(); | ||
519 | 5152 | expect($scope.confirmStorageLayout).toBe(false); | ||
520 | 5153 | }); | ||
521 | 5154 | }); | ||
522 | 5155 | |||
523 | 5156 | describe("updateStorageLayout", function() { | ||
524 | 5157 | it("calls 'applyStorageLayout'", function() { | ||
525 | 5158 | makeController(); | ||
526 | 5159 | spyOn(MachinesManager, "applyStorageLayout").and.callFake(function() { | ||
527 | 5160 | var deferred = $q.defer(); | ||
528 | 5161 | return deferred.promise; | ||
529 | 5162 | }); | ||
530 | 5163 | $scope.newLayout = { | ||
531 | 5164 | id: "flat", | ||
532 | 5165 | name: "Flat" | ||
533 | 5166 | }; | ||
534 | 5167 | $scope.updateStorageLayout($scope.newLayout); | ||
535 | 5168 | expect(MachinesManager.applyStorageLayout).toHaveBeenCalled(); | ||
536 | 5169 | }); | ||
537 | 5170 | |||
538 | 5171 | it("calls 'closeStorageLayoutConfirm'", function() { | ||
539 | 5172 | makeController(); | ||
540 | 5173 | spyOn(MachinesManager, "applyStorageLayout").and.callFake(function() { | ||
541 | 5174 | var deferred = $q.defer(); | ||
542 | 5175 | return deferred.promise; | ||
543 | 5176 | }); | ||
544 | 5177 | spyOn($scope, "closeStorageLayoutConfirm"); | ||
545 | 5178 | $scope.updateStorageLayout({ | ||
546 | 5179 | id: "flat", | ||
547 | 5180 | name: "Flat" | ||
548 | 5181 | }); | ||
549 | 5182 | expect($scope.closeStorageLayoutConfirm).toHaveBeenCalled(); | ||
550 | 5183 | }); | ||
551 | 5184 | }); | ||
552 | 5185 | |||
553 | 5186 | describe("openNewDatastorePanel", function() { | ||
554 | 5187 | it("sets 'createNewDatastore' to true", function() { | ||
555 | 5188 | makeController(); | ||
556 | 5189 | $scope.createNewDatastore = false; | ||
557 | 5190 | $scope.available = [ | ||
558 | 5191 | { | ||
559 | 5192 | $selected: true, | ||
560 | 5193 | id: 1 | ||
561 | 5194 | } | ||
562 | 5195 | ]; | ||
563 | 5196 | $scope.openNewDatastorePanel(); | ||
564 | 5197 | expect($scope.createNewDatastore).toBe(true); | ||
565 | 5198 | }); | ||
566 | 5199 | |||
567 | 5200 | it("sets newDatastore", function() { | ||
568 | 5201 | makeController(); | ||
569 | 5202 | $scope.available = [ | ||
570 | 5203 | { | ||
571 | 5204 | $selected: true, | ||
572 | 5205 | id: 1, | ||
573 | 5206 | mount_point: "dev/null", | ||
574 | 5207 | size_human: "35 GB" | ||
575 | 5208 | } | ||
576 | 5209 | ]; | ||
577 | 5210 | $scope.openNewDatastorePanel(); | ||
578 | 5211 | expect($scope.datastores.new).toEqual({ | ||
579 | 5212 | id: $scope.available[0].id, | ||
580 | 5213 | name: "", | ||
581 | 5214 | mountpoint: $scope.available[0].mount_point, | ||
582 | 5215 | filesystem: "VMFS6", | ||
583 | 5216 | size: $scope.available[0].size_human | ||
584 | 5217 | }); | ||
585 | 5218 | }); | ||
586 | 5219 | }); | ||
587 | 5220 | |||
588 | 5221 | describe("closeNewDatastorePanel", function() { | ||
589 | 5222 | it("sets 'createNewDatastore' to false", function() { | ||
590 | 5223 | makeController(); | ||
591 | 5224 | $scope.createNewDatastore = true; | ||
592 | 5225 | $scope.closeNewDatastorePanel(); | ||
593 | 5226 | expect($scope.createNewDatastore).toBe(false); | ||
594 | 5227 | }); | ||
595 | 5228 | |||
596 | 5229 | it("sets 'newDatastore' to '{}'", function() { | ||
597 | 5230 | makeController(); | ||
598 | 5231 | $scope.datastores.new = { id: 1, name: "" }; | ||
599 | 5232 | $scope.closeNewDatastorePanel(); | ||
600 | 5233 | expect($scope.datastores.new).toEqual({}); | ||
601 | 5234 | }); | ||
602 | 5235 | }); | ||
603 | 5236 | |||
604 | 5237 | describe("canPerformActionOnDatastoreSet", function() { | ||
605 | 5238 | it("return false if not on vmsf6 storage layout", function() { | ||
606 | 5239 | makeController(); | ||
607 | 5240 | $scope.addToExistingDatastore = false; | ||
608 | 5241 | $scope.createNewDatastore = false; | ||
609 | 5242 | $scope.selectedAvailableDatastores = [1]; | ||
610 | 5243 | $scope.storageLayout = { id: "flat" }; | ||
611 | 5244 | expect($scope.canPerformActionOnDatastoreSet()).toBe(false); | ||
612 | 5245 | }); | ||
613 | 5246 | |||
614 | 5247 | it("return false if already editing datastores", function() { | ||
615 | 5248 | makeController(); | ||
616 | 5249 | $scope.addToExistingDatastore = false; | ||
617 | 5250 | $scope.createNewDatastore = true; | ||
618 | 5251 | $scope.selectedAvailableDatastores = [1]; | ||
619 | 5252 | $scope.storageLayout = { id: "vmfs6" }; | ||
620 | 5253 | expect($scope.canPerformActionOnDatastoreSet()).toBe(false); | ||
621 | 5254 | }); | ||
622 | 5255 | |||
623 | 5256 | it("return false if no device is selected", function() { | ||
624 | 5257 | makeController(); | ||
625 | 5258 | $scope.addToExistingDatastore = false; | ||
626 | 5259 | $scope.createNewDatastore = false; | ||
627 | 5260 | $scope.selectedAvailableDatastores = []; | ||
628 | 5261 | $scope.storageLayout = { id: "vmfs6" }; | ||
629 | 5262 | expect($scope.canPerformActionOnDatastoreSet()).toBe(false); | ||
630 | 5263 | }); | ||
631 | 5264 | |||
632 | 5265 | it("return true when conditions are matched", function() { | ||
633 | 5266 | makeController(); | ||
634 | 5267 | $scope.addToExistingDatastore = false; | ||
635 | 5268 | $scope.createNewDatastore = false; | ||
636 | 5269 | $scope.selectedAvailableDatastores = [1]; | ||
637 | 5270 | $scope.storageLayout = { id: "vmfs6" }; | ||
638 | 5271 | expect($scope.canPerformActionOnDatastoreSet()).toBe(true); | ||
639 | 5272 | }); | ||
640 | 5273 | }); | ||
641 | 5274 | |||
642 | 5275 | describe("checkAddToDatastoreValid", function() { | ||
643 | 5276 | it("selected disks are valid when that condition is true", function() { | ||
644 | 5277 | makeController(); | ||
645 | 5278 | var selected = { | ||
646 | 5279 | has_partitions: false | ||
647 | 5280 | }; | ||
648 | 5281 | spyOn($scope, "getSelectedAvailable").and.returnValue([selected]); | ||
649 | 5282 | expect($scope.addToDatastoreValid).toBe(false); | ||
650 | 5283 | $scope.checkAddToDatastoreValid(); | ||
651 | 5284 | expect($scope.addToDatastoreValid).toBe(true); | ||
652 | 5285 | }); | ||
653 | 5286 | |||
654 | 5287 | it("selected disks are not valid disk has a partition", function() { | ||
655 | 5288 | makeController(); | ||
656 | 5289 | var selected = { | ||
657 | 5290 | has_partitions: true | ||
658 | 5291 | }; | ||
659 | 5292 | spyOn($scope, "getSelectedAvailable").and.returnValue([selected]); | ||
660 | 5293 | expect($scope.addToDatastoreValid).toBe(false); | ||
661 | 5294 | $scope.checkAddToDatastoreValid(); | ||
662 | 5295 | expect($scope.addToDatastoreValid).toBe(false); | ||
663 | 5296 | }); | ||
664 | 5297 | |||
665 | 5298 | it("selected disks are not valid when no selected disks", function() { | ||
666 | 5299 | makeController(); | ||
667 | 5300 | spyOn($scope, "getSelectedAvailable").and.returnValue([]); | ||
668 | 5301 | expect($scope.addToDatastoreValid).toBe(false); | ||
669 | 5302 | $scope.checkAddToDatastoreValid(); | ||
670 | 5303 | expect($scope.addToDatastoreValid).toBe(false); | ||
671 | 5304 | }); | ||
672 | 5305 | }); | ||
673 | 5306 | |||
674 | 5307 | describe("openAddToExistingDatastorePanel", function() { | ||
675 | 5308 | it("sets 'addToExistingDatastore' to true", function() { | ||
676 | 5309 | makeController(); | ||
677 | 5310 | $scope.addToExistingDatastore = false; | ||
678 | 5311 | $scope.available = [ | ||
679 | 5312 | { | ||
680 | 5313 | $selected: true, | ||
681 | 5314 | id: 1 | ||
682 | 5315 | } | ||
683 | 5316 | ]; | ||
684 | 5317 | $scope.openAddToExistingDatastorePanel(); | ||
685 | 5318 | expect($scope.addToExistingDatastore).toBe(true); | ||
686 | 5319 | }); | ||
687 | 5320 | |||
688 | 5321 | it("sets 'selectedAvailableDatastores' to selected", function() { | ||
689 | 5322 | makeController(); | ||
690 | 5323 | $scope.datastores.old = [ | ||
691 | 5324 | { | ||
692 | 5325 | $selected: true, | ||
693 | 5326 | id: 1 | ||
694 | 5327 | } | ||
695 | 5328 | ]; | ||
696 | 5329 | $scope.openAddToExistingDatastorePanel(); | ||
697 | 5330 | expect($scope.selectedAvailableDatastores).toEqual($scope.available); | ||
698 | 5331 | }); | ||
699 | 5332 | |||
700 | 5333 | it("sets 'datastores.old' to first disk", function() { | ||
701 | 5334 | makeController(); | ||
702 | 5335 | $scope.openAddToExistingDatastorePanel(); | ||
703 | 5336 | expect($scope.datastores.old).toBe($scope.node.disks[0]); | ||
704 | 5337 | }); | ||
705 | 5338 | }); | ||
706 | 5339 | |||
707 | 5340 | describe("closeAddToExistingDatastorePanel", function() { | ||
708 | 5341 | it("sets 'addToExistingDatastore' to false", function() { | ||
709 | 5342 | makeController(); | ||
710 | 5343 | $scope.addToExistingDatastore = true; | ||
711 | 5344 | $scope.closeAddToExistingDatastorePanel(); | ||
712 | 5345 | expect($scope.addToExistingDatastore).toBe(false); | ||
713 | 5346 | }); | ||
714 | 5347 | |||
715 | 5348 | it("sets, 'newDatasore' to '{}'", function() { | ||
716 | 5349 | makeController(); | ||
717 | 5350 | $scope.datastores.new = { id: 1, name: "" }; | ||
718 | 5351 | $scope.closeAddToExistingDatastorePanel(); | ||
719 | 5352 | expect($scope.datastores.new).toEqual({}); | ||
720 | 5353 | }); | ||
721 | 5354 | }); | ||
722 | 5355 | |||
723 | 5356 | describe("createDatastore", function() { | ||
724 | 5357 | it("sets 'createNewDatastore' to true", function() { | ||
725 | 5358 | makeController(); | ||
726 | 5359 | spyOn(MachinesManager, "createDatastore").and.callFake(function() { | ||
727 | 5360 | var deferred = $q.defer(); | ||
728 | 5361 | return deferred.promise; | ||
729 | 5362 | }); | ||
730 | 5363 | $scope.createNewDatastore = false; | ||
731 | 5364 | $scope.createDatastore(); | ||
732 | 5365 | expect($scope.createNewDatastore).toBe(true); | ||
733 | 5366 | }); | ||
734 | 5367 | |||
735 | 5368 | it("calls 'MachinesManager.createDatastore'", function() { | ||
736 | 5369 | makeController(); | ||
737 | 5370 | spyOn(MachinesManager, "createDatastore").and.callFake(function() { | ||
738 | 5371 | var deferred = $q.defer(); | ||
739 | 5372 | return deferred.promise; | ||
740 | 5373 | }); | ||
741 | 5374 | $scope.createDatastore(); | ||
742 | 5375 | expect(MachinesManager.createDatastore).toHaveBeenCalled(); | ||
743 | 5376 | }); | ||
744 | 5377 | }); | ||
745 | 5076 | }); | 5378 | }); |
746 | diff --git a/src/maasserver/static/js/angular/directives/nodedetails/storage_datastores.js b/src/maasserver/static/js/angular/directives/nodedetails/storage_datastores.js | |||
747 | 5077 | new file mode 100644 | 5379 | new file mode 100644 |
748 | index 0000000..d040063 | |||
749 | --- /dev/null | |||
750 | +++ b/src/maasserver/static/js/angular/directives/nodedetails/storage_datastores.js | |||
751 | @@ -0,0 +1,9 @@ | |||
752 | 1 | function storageDatastores() { | ||
753 | 2 | const path = "static/partials/nodedetails/storage/datastores.html"; | ||
754 | 3 | return { | ||
755 | 4 | restrict: "E", | ||
756 | 5 | templateUrl: `${path}?v=${MAAS_config.files_version}` | ||
757 | 6 | }; | ||
758 | 7 | } | ||
759 | 8 | |||
760 | 9 | export default storageDatastores; | ||
761 | diff --git a/src/maasserver/static/js/angular/entry.js b/src/maasserver/static/js/angular/entry.js | |||
762 | index 0cf83dc..cea8d08 100644 | |||
763 | --- a/src/maasserver/static/js/angular/entry.js | |||
764 | +++ b/src/maasserver/static/js/angular/entry.js | |||
765 | @@ -36,7 +36,8 @@ import { | |||
766 | 36 | } from "./controllers/node_details_networking"; // TODO: fix export/namespace | 36 | } from "./controllers/node_details_networking"; // TODO: fix export/namespace |
767 | 37 | // prettier-ignore | 37 | // prettier-ignore |
768 | 38 | import { | 38 | import { |
770 | 39 | removeAvailableByNew | 39 | removeAvailableByNew, |
771 | 40 | datastoresOnly | ||
772 | 40 | } from "./controllers/node_details_storage"; // TODO: fix export/namespace | 41 | } from "./controllers/node_details_storage"; // TODO: fix export/namespace |
773 | 41 | // prettier-ignore | 42 | // prettier-ignore |
774 | 42 | import { | 43 | import { |
775 | @@ -152,6 +153,7 @@ import ZonesListController from "./controllers/zones_list"; | |||
776 | 152 | import storageDisksPartitions | 153 | import storageDisksPartitions |
777 | 153 | from "./directives/nodedetails/storage_disks_partitions"; | 154 | from "./directives/nodedetails/storage_disks_partitions"; |
778 | 154 | import storageFilesystems from "./directives/nodedetails/storage_filesystems"; | 155 | import storageFilesystems from "./directives/nodedetails/storage_filesystems"; |
779 | 156 | import storageDatastores from "./directives/nodedetails/storage_datastores"; | ||
780 | 155 | import maasMachinesTable from "./directives/machines_table"; | 157 | import maasMachinesTable from "./directives/machines_table"; |
781 | 156 | import addMachine from "./directives/nodelist/add_machine"; | 158 | import addMachine from "./directives/nodelist/add_machine"; |
782 | 157 | import maasAccordion from "./directives/accordion"; | 159 | import maasAccordion from "./directives/accordion"; |
783 | @@ -493,6 +495,7 @@ angular | |||
784 | 493 | .filter("removeDefaultVLANIfVLAN", removeDefaultVLANIfVLAN) | 495 | .filter("removeDefaultVLANIfVLAN", removeDefaultVLANIfVLAN) |
785 | 494 | .filter("filterLinkModes", filterLinkModes) | 496 | .filter("filterLinkModes", filterLinkModes) |
786 | 495 | .filter("removeAvailableByNew", removeAvailableByNew) | 497 | .filter("removeAvailableByNew", removeAvailableByNew) |
787 | 498 | .filter("datastoresOnly", datastoresOnly) | ||
788 | 496 | .filter("filterSource", filterSource) | 499 | .filter("filterSource", filterSource) |
789 | 497 | .filter("ignoreSelf", ignoreSelf) | 500 | .filter("ignoreSelf", ignoreSelf) |
790 | 498 | .filter("removeNoDHCP", removeNoDHCP) | 501 | .filter("removeNoDHCP", removeNoDHCP) |
791 | @@ -594,6 +597,7 @@ angular | |||
792 | 594 | // directives | 597 | // directives |
793 | 595 | .directive("storageDisksPartitions", storageDisksPartitions) | 598 | .directive("storageDisksPartitions", storageDisksPartitions) |
794 | 596 | .directive("storageFilesystems", storageFilesystems) | 599 | .directive("storageFilesystems", storageFilesystems) |
795 | 600 | .directive("storageDatastores", storageDatastores) | ||
796 | 597 | .directive("addMachine", addMachine) | 601 | .directive("addMachine", addMachine) |
797 | 598 | .directive("maasAccordion", maasAccordion) | 602 | .directive("maasAccordion", maasAccordion) |
798 | 599 | .directive("maasActionButton", maasActionButton) | 603 | .directive("maasActionButton", maasActionButton) |
799 | diff --git a/src/maasserver/static/js/angular/factories/machines.js b/src/maasserver/static/js/angular/factories/machines.js | |||
800 | index c3c3a5b..e70b096 100644 | |||
801 | --- a/src/maasserver/static/js/angular/factories/machines.js | |||
802 | +++ b/src/maasserver/static/js/angular/factories/machines.js | |||
803 | @@ -77,6 +77,21 @@ function MachinesManager(RegionConnection, NodesManager) { | |||
804 | 77 | return RegionConnection.callMethod(method, params); | 77 | return RegionConnection.callMethod(method, params); |
805 | 78 | }; | 78 | }; |
806 | 79 | 79 | ||
807 | 80 | MachinesManager.prototype.applyStorageLayout = function(params) { | ||
808 | 81 | var method = this._handler + ".apply_storage_layout"; | ||
809 | 82 | return RegionConnection.callMethod(method, params); | ||
810 | 83 | }; | ||
811 | 84 | |||
812 | 85 | MachinesManager.prototype.createDatastore = function(params) { | ||
813 | 86 | var method = this._handler + ".create_vmfs_datastore"; | ||
814 | 87 | return RegionConnection.callMethod(method, params); | ||
815 | 88 | }; | ||
816 | 89 | |||
817 | 90 | MachinesManager.prototype.updateDatastore = function(params) { | ||
818 | 91 | var method = this._handler + ".update_vmfs_datastore"; | ||
819 | 92 | return RegionConnection.callMethod(method, params); | ||
820 | 93 | }; | ||
821 | 94 | |||
822 | 80 | return new MachinesManager(); | 95 | return new MachinesManager(); |
823 | 81 | } | 96 | } |
824 | 82 | 97 | ||
825 | diff --git a/src/maasserver/static/js/angular/factories/tests/test_machines.js b/src/maasserver/static/js/angular/factories/tests/test_machines.js | |||
826 | index 26eff00..9d4f7b6 100644 | |||
827 | --- a/src/maasserver/static/js/angular/factories/tests/test_machines.js | |||
828 | +++ b/src/maasserver/static/js/angular/factories/tests/test_machines.js | |||
829 | @@ -87,4 +87,56 @@ describe("MachinesManager", function() { | |||
830 | 87 | ); | 87 | ); |
831 | 88 | }); | 88 | }); |
832 | 89 | }); | 89 | }); |
833 | 90 | |||
834 | 91 | describe("applyStorageLayout", function() { | ||
835 | 92 | it("calls apply_storage_layout", function() { | ||
836 | 93 | spyOn(RegionConnection, "callMethod"); | ||
837 | 94 | var params = { | ||
838 | 95 | system_id: makeName("system-id"), | ||
839 | 96 | mount_point: makeName("/dir") | ||
840 | 97 | }; | ||
841 | 98 | MachinesManager.applyStorageLayout(params); | ||
842 | 99 | expect(RegionConnection.callMethod).toHaveBeenCalledWith( | ||
843 | 100 | "machine.apply_storage_layout", | ||
844 | 101 | params | ||
845 | 102 | ); | ||
846 | 103 | }); | ||
847 | 104 | }); | ||
848 | 105 | |||
849 | 106 | describe("createDatastore", function() { | ||
850 | 107 | it("calls create_vmfs_datastore", function() { | ||
851 | 108 | spyOn(RegionConnection, "callMethod"); | ||
852 | 109 | var params = { | ||
853 | 110 | system_id: makeName("system-id"), | ||
854 | 111 | block_devices: [1, 2, 3, 5], | ||
855 | 112 | partitions: [5, 6, 7, 8], | ||
856 | 113 | name: "New datastore" | ||
857 | 114 | }; | ||
858 | 115 | MachinesManager.createDatastore(params); | ||
859 | 116 | expect(RegionConnection.callMethod).toHaveBeenCalledWith( | ||
860 | 117 | "machine.create_vmfs_datastore", | ||
861 | 118 | params | ||
862 | 119 | ); | ||
863 | 120 | }); | ||
864 | 121 | }); | ||
865 | 122 | |||
866 | 123 | describe("updateDatastore", function() { | ||
867 | 124 | it("calls update_vmfs_datastore", function() { | ||
868 | 125 | spyOn(RegionConnection, "callMethod"); | ||
869 | 126 | var params = { | ||
870 | 127 | system_id: makeName("system-id"), | ||
871 | 128 | add_block_devices: [1, 2, 3, 4], | ||
872 | 129 | add_partitions: [5, 6, 7, 8], | ||
873 | 130 | remove_partitions: [], | ||
874 | 131 | remove_block_devices: [], | ||
875 | 132 | name: "New datastore", | ||
876 | 133 | vmfs_datastore_id: 1 | ||
877 | 134 | }; | ||
878 | 135 | MachinesManager.updateDatastore(params); | ||
879 | 136 | expect(RegionConnection.callMethod).toHaveBeenCalledWith( | ||
880 | 137 | "machine.update_vmfs_datastore", | ||
881 | 138 | params | ||
882 | 139 | ); | ||
883 | 140 | }); | ||
884 | 141 | }); | ||
885 | 90 | }); | 142 | }); |
886 | diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html | |||
887 | index d722404..5612ef2 100755 | |||
888 | --- a/src/maasserver/static/partials/node-details.html | |||
889 | +++ b/src/maasserver/static/partials/node-details.html | |||
890 | @@ -69,7 +69,7 @@ | |||
891 | 69 | <p class="page-header__message"><i class="p-icon--warning">Warning:</i> MAAS is not providing DHCP.</p> | 69 | <p class="page-header__message"><i class="p-icon--warning">Warning:</i> MAAS is not providing DHCP.</p> |
892 | 70 | </div> | 70 | </div> |
893 | 71 | </div> | 71 | </div> |
895 | 72 | <div class="row ng-hide u-no-margin--top" data-ng-hide="isActionError() || isDeployError() || hasActionPowerError(action.option.name)"> | 72 | <div class="row ng-hide u-no-margin--top" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name)"> |
896 | 73 | <!-- XXX blake_r 2015-02-19 - Need to add e2e test. --> | 73 | <!-- XXX blake_r 2015-02-19 - Need to add e2e test. --> |
897 | 74 | <div class="page-header__section"> | 74 | <div class="page-header__section"> |
898 | 75 | <form class="p-form"> | 75 | <form class="p-form"> |
899 | @@ -110,7 +110,7 @@ | |||
900 | 110 | </label> | 110 | </label> |
901 | 111 | <span data-ng-if="!nodesManager.isModernUbuntu(osSelection)"> | 111 | <span data-ng-if="!nodesManager.isModernUbuntu(osSelection)"> |
902 | 112 | <i class="p-icon--warning"></i> | 112 | <i class="p-icon--warning"></i> |
904 | 113 | <strong>Warning:</strong> Ubuntu 18.04 is the minimum required to create a KVM host. <a target="_blank" | 113 | <strong>Warning:</strong> Ubuntu 18.04 is the minimum required. <a target="_blank" |
905 | 114 | class="p-link--external" | 114 | class="p-link--external" |
906 | 115 | href="https://docs.maas.io/2.5/en/manage-pods-webui#add-a-kvm-host">Learn more</a> | 115 | href="https://docs.maas.io/2.5/en/manage-pods-webui#add-a-kvm-host">Learn more</a> |
907 | 116 | </span> | 116 | </span> |
908 | @@ -122,11 +122,6 @@ | |||
909 | 122 | </div> | 122 | </div> |
910 | 123 | </div> | 123 | </div> |
911 | 124 | </div> | 124 | </div> |
912 | 125 | <div data-ng-if="isSSHKeyWarning()" class="p-strip is-shallow u-no-padding--top"> | ||
913 | 126 | <p class="u-remove-max-width"> | ||
914 | 127 | <i class="p-icon--warning">Warning:</i> Login will not be possible because no SSH keys have been added to your account. To add an SSH key, visit <a href="account/prefs/">your account page</a>. | ||
915 | 128 | </p> | ||
916 | 129 | </div> | ||
917 | 130 | </div> | 125 | </div> |
918 | 131 | </div> | 126 | </div> |
919 | 132 | </div> | 127 | </div> |
920 | @@ -172,7 +167,7 @@ | |||
921 | 172 | </form> | 167 | </form> |
922 | 173 | </div> | 168 | </div> |
923 | 174 | </div> | 169 | </div> |
925 | 175 | <div class="row ng-hide" data-ng-hide="isActionError() || isDeployError() || hasActionPowerError(action.option.name) || action.option.name !== 'commission'" data-ng-if="hasCustomCommissioningScripts()"> | 170 | <div class="row ng-hide" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name) || action.option.name !== 'commission'" data-ng-if="hasCustomCommissioningScripts()"> |
926 | 176 | <div class="page-header__section"> | 171 | <div class="page-header__section"> |
927 | 177 | <form class="p-form"> | 172 | <form class="p-form"> |
928 | 178 | <div class="col-8"> | 173 | <div class="col-8"> |
929 | @@ -184,7 +179,7 @@ | |||
930 | 184 | </form> | 179 | </form> |
931 | 185 | </div> | 180 | </div> |
932 | 186 | </div> | 181 | </div> |
934 | 187 | <div class="row u-no-margin--top ng-hide" data-ng-hide="isActionError() || isDeployError() || hasActionPowerError(action.option.name) || (action.option.name !== 'commission' && action.option.name !== 'test')"> | 182 | <div class="row u-no-margin--top ng-hide" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name) || (action.option.name !== 'commission' && action.option.name !== 'test')"> |
935 | 188 | <hr /> | 183 | <hr /> |
936 | 189 | <div class="page-header__section"> | 184 | <div class="page-header__section"> |
937 | 190 | <form class="p-form"> | 185 | <form class="p-form"> |
938 | @@ -197,7 +192,7 @@ | |||
939 | 197 | </form> | 192 | </form> |
940 | 198 | </div> | 193 | </div> |
941 | 199 | </div> | 194 | </div> |
943 | 200 | <div class="u-no-margin--top row ng-hide" data-ng-hide="isActionError() || isDeployError() || hasActionPowerError(action.option.name)" data-ng-if="action.option.name === 'commission' || action.option.name === 'test' && !action.showing_confirmation"> | 195 | <div class="u-no-margin--top row ng-hide" data-ng-hide="isActionError() || isDeployError() || isSSHKeyError() || hasActionPowerError(action.option.name)" data-ng-if="action.option.name === 'commission' || action.option.name === 'test' && !action.showing_confirmation"> |
944 | 201 | <hr /> | 196 | <hr /> |
945 | 202 | <div class="page-header__section col-12"> | 197 | <div class="page-header__section col-12"> |
946 | 203 | <form class="p-form"> | 198 | <form class="p-form"> |
947 | @@ -219,18 +214,18 @@ | |||
948 | 219 | <div data-ng-repeat="confirmation_detail in action.confirmation_details"> | 214 | <div data-ng-repeat="confirmation_detail in action.confirmation_details"> |
949 | 220 | <span>{$ confirmation_detail $}</span> | 215 | <span>{$ confirmation_detail $}</span> |
950 | 221 | </div> | 216 | </div> |
963 | 222 | <div class="u-equal-height"> | 217 | <div class="u-equal-height"> |
964 | 223 | <div class="col-9 u-vertically-center"> | 218 | <div class="col-9 u-vertically-center"> |
965 | 224 | <p class="u-remove-max-width"> | 219 | <p class="u-remove-max-width"> |
966 | 225 | Are you sure you want to {$ action.actionOption.name $} this {$ type_name $}? | 220 | Are you sure you want to {$ action.actionOption.name $} this {$ type_name $}? |
967 | 226 | </p> | 221 | </p> |
968 | 227 | </div> | 222 | </div> |
969 | 228 | <div class="col-3"> | 223 | <div class="col-3"> |
970 | 229 | <div class="u-align--right"> | 224 | <div class="u-align--right"> |
971 | 230 | <button class="p-button--base" data-ng-click="actionCancel()">No</button> | 225 | <button class="p-button--base" data-ng-click="actionCancel()">No</button> |
972 | 231 | <button class="p-button--negative" data-ng-click="actionGo()">Yes</button> | 226 | <button class="p-button--negative" data-ng-click="actionGo()">Yes</button> |
973 | 232 | </div> | 227 | </div> |
974 | 233 | </div> | 228 | </div> |
975 | 234 | </div> | 229 | </div> |
976 | 235 | </div> | 230 | </div> |
977 | 236 | </div> | 231 | </div> |
978 | @@ -273,6 +268,18 @@ | |||
979 | 273 | </div> | 268 | </div> |
980 | 274 | </div> | 269 | </div> |
981 | 275 | </div> | 270 | </div> |
982 | 271 | <div class="row u-equal-height ng-hide" data-ng-show="!isDeployError() && isSSHKeyError()"> | ||
983 | 272 | <div class="col-9 u-vertically-center"> | ||
984 | 273 | <p class="u-remove-max-width"> | ||
985 | 274 | <i class="p-icon--error">Error:</i> Node cannot be {$ action.option.sentence $}, because an SSH key has not been added to your account. To add an SSH key, visit <a href="account/prefs/">your account page</a>. | ||
986 | 275 | </p> | ||
987 | 276 | </div> | ||
988 | 277 | <div class="col-3"> | ||
989 | 278 | <div class="u-align--right"> | ||
990 | 279 | <button class="p-button--base" data-ng-click="actionCancel()">Cancel</button> | ||
991 | 280 | </div> | ||
992 | 281 | </div> | ||
993 | 282 | </div> | ||
994 | 276 | </div> | 283 | </div> |
995 | 277 | 284 | ||
996 | 278 | <nav class="p-tabs u-hr--fixed-width"> | 285 | <nav class="p-tabs u-hr--fixed-width"> |
997 | @@ -394,97 +401,97 @@ | |||
998 | 394 | </div> | 401 | </div> |
999 | 395 | <div class="p-strip is-shallow"> | 402 | <div class="p-strip is-shallow"> |
1000 | 396 | <div class="row u-equal-height" data-ng-if="isDevice"> | 403 | <div class="row u-equal-height" data-ng-if="isDevice"> |
1002 | 397 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 404 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1003 | 398 | <div maas-card-loader="device"></div> | 405 | <div maas-card-loader="device"></div> |
1006 | 399 | </div> | 406 | </div> |
1007 | 400 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 407 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1008 | 401 | <div maas-card-loader="tags"></div> | 408 | <div maas-card-loader="tags"></div> |
1010 | 402 | </div> | 409 | </div> |
1011 | 403 | </div> | 410 | </div> |
1012 | 404 | <div class="row u-equal-height" data-ng-if="node.node_type == 3"> | 411 | <div class="row u-equal-height" data-ng-if="node.node_type == 3"> |
1014 | 405 | <div class="col-4 p-card--highlighted action-card u-border--solid"> | 412 | <div class="col-4 p-card--highlighted action-card u-border--solid"> |
1015 | 406 | <div maas-card-loader="services"></div> | 413 | <div maas-card-loader="services"></div> |
1018 | 407 | </div> | 414 | </div> |
1019 | 408 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 415 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1020 | 409 | <div maas-card-loader="controller"></div> | 416 | <div maas-card-loader="controller"></div> |
1023 | 410 | </div> | 417 | </div> |
1024 | 411 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 418 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1025 | 412 | <div maas-card-loader="hardware_info"></div> | 419 | <div maas-card-loader="hardware_info"></div> |
1027 | 413 | </div> | 420 | </div> |
1028 | 414 | </div> | 421 | </div> |
1029 | 415 | <div class="row u-equal-height" data-ng-if="node.node_type == 3"> | 422 | <div class="row u-equal-height" data-ng-if="node.node_type == 3"> |
1031 | 416 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.cpu_test_status === 3 }"> | 423 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.cpu_test_status === 3 }"> |
1032 | 417 | <div maas-card-loader="cpu"></div> | 424 | <div maas-card-loader="cpu"></div> |
1035 | 418 | </div> | 425 | </div> |
1036 | 419 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.memory_test_status === 3 }"> | 426 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.memory_test_status === 3 }"> |
1037 | 420 | <div maas-card-loader="memory"></div> | 427 | <div maas-card-loader="memory"></div> |
1040 | 421 | </div> | 428 | </div> |
1041 | 422 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.storage_test_status === 3 }"> | 429 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.storage_test_status === 3 }"> |
1042 | 423 | <div maas-card-loader="storage"></div> | 430 | <div maas-card-loader="storage"></div> |
1044 | 424 | </div> | 431 | </div> |
1045 | 425 | </div> | 432 | </div> |
1046 | 426 | <div class="row u-equal-height" data-ng-if="node.node_type == 3"> | 433 | <div class="row u-equal-height" data-ng-if="node.node_type == 3"> |
1048 | 427 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 434 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1049 | 428 | <div maas-card-loader="tags"></div> | 435 | <div maas-card-loader="tags"></div> |
1051 | 429 | </div> | 436 | </div> |
1052 | 430 | </div> | 437 | </div> |
1053 | 431 | <div class="row u-equal-height" data-ng-if="node.node_type == 2 || node.node_type == 4"> | 438 | <div class="row u-equal-height" data-ng-if="node.node_type == 2 || node.node_type == 4"> |
1055 | 432 | <div class="col-4 p-card--highlighted action-card u-border--solid"> | 439 | <div class="col-4 p-card--highlighted action-card u-border--solid"> |
1056 | 433 | <div maas-card-loader="services"></div> | 440 | <div maas-card-loader="services"></div> |
1059 | 434 | </div> | 441 | </div> |
1060 | 435 | <div class="col-8"> | 442 | <div class="col-8"> |
1061 | 436 | <div class="row u-equal-height"> | 443 | <div class="row u-equal-height"> |
1063 | 437 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 444 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1064 | 438 | <div maas-card-loader="images"></div> | 445 | <div maas-card-loader="images"></div> |
1067 | 439 | </div> | 446 | </div> |
1068 | 440 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 447 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1069 | 441 | <div maas-card-loader="controller"></div> | 448 | <div maas-card-loader="controller"></div> |
1071 | 442 | </div> | 449 | </div> |
1072 | 443 | </div> | 450 | </div> |
1073 | 444 | <div class="row u-equal-height"> | 451 | <div class="row u-equal-height"> |
1075 | 445 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 452 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1076 | 446 | <div maas-card-loader="hardware_info"></div> | 453 | <div maas-card-loader="hardware_info"></div> |
1079 | 447 | </div> | 454 | </div> |
1080 | 448 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> | 455 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid"> |
1081 | 449 | <div maas-card-loader="tags"></div> | 456 | <div maas-card-loader="tags"></div> |
1083 | 450 | </div> | 457 | </div> |
1084 | 458 | </div> | ||
1085 | 451 | </div> | 459 | </div> |
1086 | 452 | </div> | ||
1087 | 453 | </div> | 460 | </div> |
1088 | 454 | <div class="row u-equal-height" data-ng-if="node.node_type == 2 || node.node_type == 4"> | 461 | <div class="row u-equal-height" data-ng-if="node.node_type == 2 || node.node_type == 4"> |
1090 | 455 | <div class="col-4 p-card--highlighted action-card u-border--solid" data-ng-class="{ 'is-error': node.cpu_test_status === 3 }"> | 462 | <div class="col-4 p-card--highlighted action-card u-border--solid" data-ng-class="{ 'is-error': node.cpu_test_status === 3 }"> |
1091 | 456 | <div maas-card-loader="cpu"></div> | 463 | <div maas-card-loader="cpu"></div> |
1094 | 457 | </div> | 464 | </div> |
1095 | 458 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.memory_test_status === 3 }"> | 465 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.memory_test_status === 3 }"> |
1096 | 459 | <div maas-card-loader="memory"></div> | 466 | <div maas-card-loader="memory"></div> |
1099 | 460 | </div> | 467 | </div> |
1100 | 461 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.storage_test_status === 3 }"> | 468 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-class="{ 'is-error': node.storage_test_status === 3 }"> |
1101 | 462 | <div maas-card-loader="storage"></div> | 469 | <div maas-card-loader="storage"></div> |
1103 | 463 | </div> | 470 | </div> |
1104 | 464 | </div> | 471 | </div> |
1105 | 465 | <div class="row u-equal-height" data-ng-if="!isDevice && !isController"> | 472 | <div class="row u-equal-height" data-ng-if="!isDevice && !isController"> |
1107 | 466 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController" data-ng-class="{ 'is-error': node.cpu_test_status === 3 }"> | 473 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController" data-ng-class="{ 'is-error': node.cpu_test_status === 3 }"> |
1108 | 467 | <div maas-card-loader="cpu"></div> | 474 | <div maas-card-loader="cpu"></div> |
1111 | 468 | </div> | 475 | </div> |
1112 | 469 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController" data-ng-class="{ 'is-error': node.memory_test_status === 3 }"> | 476 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController" data-ng-class="{ 'is-error': node.memory_test_status === 3 }"> |
1113 | 470 | <div maas-card-loader="memory"></div> | 477 | <div maas-card-loader="memory"></div> |
1116 | 471 | </div> | 478 | </div> |
1117 | 472 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="node.node_type != 3" data-ng-class="{ 'is-error': node.storage_test_status === 3 }"> | 479 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="node.node_type != 3" data-ng-class="{ 'is-error': node.storage_test_status === 3 }"> |
1118 | 473 | <div maas-card-loader="storage"></div> | 480 | <div maas-card-loader="storage"></div> |
1120 | 474 | </div> | 481 | </div> |
1121 | 475 | </div> | 482 | </div> |
1122 | 476 | <div class="row u-equal-height" data-ng-if="!isDevice && !isController"> | 483 | <div class="row u-equal-height" data-ng-if="!isDevice && !isController"> |
1124 | 477 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController"> | 484 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController"> |
1125 | 478 | <div maas-card-loader="machine"></div> | 485 | <div maas-card-loader="machine"></div> |
1128 | 479 | </div> | 486 | </div> |
1129 | 480 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController"> | 487 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController"> |
1130 | 481 | <div maas-card-loader="hardware_info"></div> | 488 | <div maas-card-loader="hardware_info"></div> |
1133 | 482 | </div> | 489 | </div> |
1134 | 483 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController"> | 490 | <div class="col-4 p-card--highlighted action-card action-card--positionable u-border--solid" data-ng-if="!isController"> |
1135 | 484 | <div maas-card-loader="tags"></div> | 491 | <div maas-card-loader="tags"></div> |
1137 | 485 | </div> | 492 | </div> |
1138 | 493 | </div> | ||
1139 | 486 | </div> | 494 | </div> |
1140 | 487 | </div> | ||
1141 | 488 | </section> | 495 | </section> |
1142 | 489 | <section class="p-strip" data-ng-if="section.area === 'containers'"> | 496 | <section class="p-strip" data-ng-if="section.area === 'containers'"> |
1143 | 490 | <div class="row"> | 497 | <div class="row"> |
1144 | @@ -839,7 +846,7 @@ | |||
1145 | 839 | </td> | 846 | </td> |
1146 | 840 | <td> | 847 | <td> |
1147 | 841 | <div class="u-no-margin--top" data-ng-repeat="subnet in vlanRow['subnets']"> | 848 | <div class="u-no-margin--top" data-ng-repeat="subnet in vlanRow['subnets']"> |
1149 | 842 | <a href="#/subnet/{$ subnet.id $}" title="{$ getSubnetText(subnet) $}">{$ getSubnetText(subnet) $}</a> | 849 | <a href="#/subnet/{$ subnet.id $}" title="{$ getSubnetText(subnet) $}">{$ getSubnetText(subnet) $}</a> |
1150 | 843 | </div> | 850 | </div> |
1151 | 844 | </td> | 851 | </td> |
1152 | 845 | <td> | 852 | <td> |
1153 | @@ -2890,105 +2897,197 @@ | |||
1154 | 2890 | </form> | 2897 | </form> |
1155 | 2891 | </div> | 2898 | </div> |
1156 | 2892 | </section> | 2899 | </section> |
1174 | 2893 | <section class="p-strip" data-ng-if="section.area === 'storage'"> | 2900 | |
1175 | 2894 | <form data-ng-controller="NodeStorageController"> | 2901 | <section class="p-strip is-shallow" data-ng-if="section.area === 'storage'" data-ng-controller="NodeStorageController"> |
1176 | 2895 | <div class="row"> | 2902 | <div class="row"> |
1177 | 2896 | <div class="col-12"> | 2903 | <div class="col-12"> |
1178 | 2897 | <div class="p-notification--negative ng-hide" data-ng-hide="has_disks"> | 2904 | <div class="p-notification--negative ng-hide" data-ng-hide="has_disks"> |
1179 | 2898 | <p class="p-notification__response"> | 2905 | <p class="p-notification__response"> |
1180 | 2899 | <span class="p-notification__status">Error:</span> No storage information. Commissioning this node will gather the storage information. | 2906 | <span class="p-notification__status">Error:</span> No storage information. Commissioning this node will gather the storage information. |
1181 | 2900 | </p> | 2907 | </p> |
1182 | 2901 | </div> | 2908 | </div> |
1183 | 2902 | <div class="p-notification ng-hide" data-ng-show="isAllStorageDisabled() && canEdit()"> | 2909 | <div class="p-notification ng-hide" data-ng-show="isAllStorageDisabled() && canEdit()"> |
1184 | 2903 | <p class="p-notification__response">Storage configuration cannot be modified unless the machine is Ready or Allocated.</p> | 2910 | <p class="p-notification__response">Storage configuration cannot be modified unless the machine is Ready or Allocated.</p> |
1185 | 2904 | </div> | 2911 | </div> |
1186 | 2905 | <div class="p-notification ng-hide" data-ng-show="!isUbuntuOS() && !isCentOS()"> | 2912 | <div class="p-notification ng-hide" data-ng-show="!isUbuntuOS() && !isCentOS()"> |
1187 | 2906 | <p class="p-notification__response">Custom storage configuration is only supported on Ubuntu, CentOS, and RHEL.</p> | 2913 | <p class="p-notification__response">Custom storage configuration is only supported on Ubuntu, CentOS, and RHEL.</p> |
1188 | 2907 | </div> | 2914 | </div> |
1189 | 2908 | <div class="p-notification ng-hide" data-ng-show="!isUbuntuOS()"> | 2915 | <div class="p-notification ng-hide" data-ng-show="!isUbuntuOS()"> |
1190 | 2909 | <p class="p-notification__response">Bcache and ZFS are only supported on Ubuntu.</p> | 2916 | <p class="p-notification__response">Bcache and ZFS are only supported on Ubuntu.</p> |
1191 | 2917 | </div> | ||
1192 | 2918 | <div data-ng-repeat="issue in node.storage_layout_issues" class="p-notification--negative ng-hide" data-ng-show="hasStorageLayoutIssues()"> | ||
1193 | 2919 | <p class="p-notification__response"> | ||
1194 | 2920 | <span class="p-notification__status">Error:</span> {$ issue $} | ||
1195 | 2921 | </p> | ||
1196 | 2922 | </div> | ||
1197 | 2923 | </div> | ||
1198 | 2924 | </div> | ||
1199 | 2925 | </section> | ||
1200 | 2926 | |||
1201 | 2927 | <section class="p-strip u-no-padding--bottom" data-ng-if="section.area === 'storage'" data-ng-controller="NodeStorageController"> | ||
1202 | 2928 | <form> | ||
1203 | 2929 | <div data-ng-if="node.status_code === 4 || node.status_code === 10"> | ||
1204 | 2930 | <div class="row" data-ng-if="!confirmStorageLayout"> | ||
1205 | 2931 | <div class="col-12 prefix-9 u-sv3"> | ||
1206 | 2932 | <div class="p-form__group u-align--right"> | ||
1207 | 2933 | <div data-ng-if="storageLayoutIsDisabled(osFamily.layouts) && storageLayoutIsDisabled(osFamily.layouts)"> | ||
1208 | 2934 | <button class="p-button--neutral" disabled>Change storage layout</button> | ||
1209 | 2935 | </div> | ||
1210 | 2936 | <div data-ng-if="!(storageLayoutIsDisabled(osFamily.layouts) && storageLayoutIsDisabled(osFamily.layouts))"> | ||
1211 | 2937 | <div class="p-contextual-menu" toggle-ctrl> | ||
1212 | 2938 | <button class="p-button--neutral p-contextual-menu__toggle u-no-margin--bottom" data-ng-click="toggleMenu()"> | ||
1213 | 2939 | <span data-ng-if="!updatingStorageLayout"> | ||
1214 | 2940 | Change storage layout | ||
1215 | 2941 | <i class="p-icon--chevron" data-ng-class="{'u-rotate':isToggled}"></i> | ||
1216 | 2942 | </span> | ||
1217 | 2943 | |||
1218 | 2944 | <span data-ng-if="updatingStorageLayout"> | ||
1219 | 2945 | <i class="p-icon--spinner u-animation--spin"></i> | ||
1220 | 2946 | | ||
1221 | 2947 | Updating storage layout… | ||
1222 | 2948 | </span> | ||
1223 | 2949 | </button> | ||
1224 | 2950 | <div class="p-contextual-menu__dropdown" role="menu" data-ng-show="isToggled"> | ||
1225 | 2951 | <button class="p-contextual-menu__link" data-ng-click="toggleMenu(); openStorageLayoutConfirm('flat')">Flat</button> | ||
1226 | 2952 | <button class="p-contextual-menu__link" data-ng-click="toggleMenu(); openStorageLayoutConfirm('lvm')">LVM</button> | ||
1227 | 2953 | <button class="p-contextual-menu__link" data-ng-click="toggleMenu(); openStorageLayoutConfirm('bcache')">bcache</button> | ||
1228 | 2954 | <hr class="u-no-margin--bottom"/> | ||
1229 | 2955 | <button class="p-contextual-menu__link" data-ng-click="toggleMenu(); openStorageLayoutConfirm('vmfs6')">VMFS6 (VMware ESXi)</button> | ||
1230 | 2956 | <hr class="u-no-margin--bottom"/> | ||
1231 | 2957 | <button class="p-contextual-menu__link" data-ng-click="toggleMenu(); openStorageLayoutConfirm('blank')">No storage (blank) layout</button> | ||
1232 | 2958 | </div> | ||
1233 | 2959 | </div> | ||
1234 | 2960 | </div> | ||
1235 | 2961 | </div> | ||
1236 | 2910 | </div> | 2962 | </div> |
1241 | 2911 | <div data-ng-repeat="issue in node.storage_layout_issues" class="p-notification--negative ng-hide" data-ng-show="hasStorageLayoutIssues()"> | 2963 | </div> |
1242 | 2912 | <p class="p-notification__response"> | 2964 | <div class="row" data-ng-if="confirmStorageLayout"> |
1243 | 2913 | <span class="p-notification__status">Error:</span> {$ issue $} | 2965 | <div class="p-card--highlighted"> |
1244 | 2914 | </p> | 2966 | <div class="col-6 u-no-margin--left"> |
1245 | 2967 | <div class="p-notification--caution is-subtle" data-ng-if="newLayout.id === 'vmfs6'"> | ||
1246 | 2968 | <p class="p-notification__response"> | ||
1247 | 2969 | <strong>Are you sure you want to change the storage layout to VMFS6?</strong><br /> | ||
1248 | 2970 | Any changes done already will be discarded.<br /> | ||
1249 | 2971 | This layout allows only for the deployment of <strong>VMware ESXi</strong> images.<br /> | ||
1250 | 2972 | The storage layout will be applied to a node when it is deployed. | ||
1251 | 2973 | </p> | ||
1252 | 2974 | </div> | ||
1253 | 2975 | <div class="p-notification--caution is-subtle" data-ng-if="newLayout.id === 'lvm'"> | ||
1254 | 2976 | <p class="p-notification__response"> | ||
1255 | 2977 | <strong>Are you sure you want to change the storage layout to LVM?</strong><br /> | ||
1256 | 2978 | Any changes done already will be lost.<br /> | ||
1257 | 2979 | The storage layout will be applied to a node when it is deployed. | ||
1258 | 2980 | </p> | ||
1259 | 2981 | </div> | ||
1260 | 2982 | <div class="p-notification--caution is-subtle" data-ng-if="newLayout.id === 'blank'"> | ||
1261 | 2983 | <p class="p-notification__response"> | ||
1262 | 2984 | <strong>Are you sure you want to change this storage layout to blank?</strong><br /> | ||
1263 | 2985 | Used disks will be returned to available, and any volume groups, raid sets, | ||
1264 | 2915 | caches, and filesystems removed.<br /> | 2986 | caches, and filesystems removed.<br /> |
1265 | 2987 | The storage layout will be applied to a node when it is deployed. | ||
1266 | 2988 | </p> | ||
1267 | 2989 | </div> | ||
1268 | 2990 | <div class="p-notification--caution is-subtle" data-ng-if="newLayout.id !== 'lvm' && newLayout.id !== 'vmfs6' && newLayout.id !== 'blank'"> | ||
1269 | 2991 | <p class="p-notification__response"> | ||
1270 | 2992 | <strong>Are you sure you want to change the storage layout to {$ newLayout.id $}?</strong><br /> | ||
1271 | 2993 | Any changes done already will be lost.<br /> | ||
1272 | 2994 | The storage layout will be applied to a node when it is deployed. | ||
1273 | 2995 | </p> | ||
1274 | 2996 | </div> | ||
1275 | 2997 | </div> | ||
1276 | 2998 | <div class="col-6 u-align--right"> | ||
1277 | 2999 | <button class="p-button--base" | ||
1278 | 3000 | data-ng-click="closeStorageLayoutConfirm()">Cancel</button> | ||
1279 | 3001 | <button class="p-button--negative" | ||
1280 | 3002 | data-ng-click="updateStorageLayout(storageLayout)"> | ||
1281 | 3003 | Change storage layout | ||
1282 | 3004 | </button> | ||
1283 | 3005 | </div> | ||
1284 | 2916 | </div> | 3006 | </div> |
1285 | 2917 | </div> | 3007 | </div> |
1286 | 2918 | </div> | 3008 | </div> |
1290 | 2919 | <div class="row"> | 3009 | |
1291 | 2920 | <div data-ng-controller="NodeFilesystemsController"> | 3010 | <div class="p-strip"> |
1292 | 2921 | <storage-filesystems></storage-filesystems> | 3011 | <div class="row" data-ng-if="storageLayout.id === 'vmfs6'"> |
1293 | 3012 | <div data-ng-controller="NodeFilesystemsController"> | ||
1294 | 3013 | <storage-datastores></storage-datastores> | ||
1295 | 3014 | </div> | ||
1296 | 2922 | </div> | 3015 | </div> |
1300 | 2923 | <div data-ng-show="cachesets.length"> | 3016 | |
1301 | 2924 | <div class="row"> | 3017 | <div class="row"> |
1302 | 2925 | <h3 class="p-heading--four">Available cache sets</h3> | 3018 | <div data-ng-controller="NodeFilesystemsController" data-ng-if="storageLayout.id !== 'vmfs6'"> |
1303 | 3019 | <storage-filesystems></storage-filesystems> | ||
1304 | 2926 | </div> | 3020 | </div> |
1343 | 2927 | <div class="row"> | 3021 | <div data-ng-show="cachesets.length"> |
1344 | 2928 | <table class="p-table-expanding"> | 3022 | <div class="row"> |
1345 | 2929 | <thead> | 3023 | <h3 class="p-heading--four">Available cache sets</h3> |
1346 | 2930 | <tr> | 3024 | </div> |
1347 | 2931 | <th class="col-2">Name</th> | 3025 | <div class="row"> |
1348 | 2932 | <th class="col-3">Size</th> | 3026 | <table class="p-table-expanding"> |
1349 | 2933 | <th class="col-4">Used by</th> | 3027 | <thead> |
1350 | 2934 | <th class="col-2"> | 3028 | <tr> |
1351 | 2935 | <div class="u-align--right">Actions</div> | 3029 | <th class="col-2">Name</th> |
1352 | 2936 | </th> | 3030 | <th class="col-3">Size</th> |
1353 | 2937 | </tr> | 3031 | <th class="col-4">Used by</th> |
1354 | 2938 | </thead> | 3032 | <th class="col-2"> |
1355 | 2939 | <tbody> | 3033 | <div class="u-align--right">Actions</div> |
1356 | 2940 | <tr data-ng-repeat="cacheset in cachesets" data-ng-class="{ 'is-active': cacheset.$selected }"> | 3034 | </th> |
1357 | 2941 | <td class="col-2" aria-label="Name" title="{$ cacheset.name $}">{$ cacheset.name $}</td> | 3035 | </tr> |
1358 | 2942 | <td class="col-3" aria-label="Size" title="{$ cacheset.size_human $}">{$ cacheset.size_human $}</td> | 3036 | </thead> |
1359 | 2943 | <td class="col-4" aria-label="Used by" title="{$ cacheset.used_by $}">{$ cacheset.used_by $}</td> | 3037 | <tbody> |
1360 | 2944 | <td class="col-2 p-table--action-cell"> | 3038 | <tr data-ng-repeat="cacheset in cachesets" data-ng-class="{ 'is-active': cacheset.$selected }"> |
1361 | 2945 | <div class="u-align--right"> | 3039 | <td class="col-2" aria-label="Name" title="{$ cacheset.name $}">{$ cacheset.name $}</td> |
1362 | 2946 | <div class="p-contextual-menu" toggle-ctrl data-ng-if="canDeleteCacheSet(cacheset) && !cacheset.$selected"> | 3040 | <td class="col-3" aria-label="Size" title="{$ cacheset.size_human $}">{$ cacheset.size_human $}</td> |
1363 | 2947 | <button class="p-button--base is-small p-contextual-menu__toggle" data-ng-click="toggleMenu()"> | 3041 | <td class="col-4" aria-label="Used by" title="{$ cacheset.used_by $}">{$ cacheset.used_by $}</td> |
1364 | 2948 | <i class="p-icon--contextual-menu u-no-margin--right">Actions</i> | 3042 | <td class="col-2 p-table--action-cell"> |
1365 | 2949 | </button> | 3043 | <div class="u-align--right"> |
1366 | 2950 | <div class="p-contextual-menu__dropdown" role="menu" data-ng-show="isToggled"> | 3044 | <div class="p-contextual-menu" toggle-ctrl data-ng-if="canDeleteCacheSet(cacheset) && !cacheset.$selected"> |
1367 | 2951 | <button class="p-contextual-menu__link" | 3045 | <button class="p-button--base is-small p-contextual-menu__toggle" data-ng-click="toggleMenu()"> |
1368 | 2952 | aria-label="Remove" | 3046 | <i class="p-icon--contextual-menu u-no-margin--right">Actions</i> |
1369 | 2953 | data-ng-show="canDeleteCacheSet(cacheset)" | 3047 | </button> |
1370 | 2954 | data-ng-click="toggleMenu(); quickCacheSetDelete(cacheset)">Remove…</button> | 3048 | <div class="p-contextual-menu__dropdown" role="menu" data-ng-show="isToggled"> |
1371 | 2955 | </div> | 3049 | <button class="p-contextual-menu__link" |
1372 | 2956 | </div> | 3050 | aria-label="Remove" |
1373 | 2957 | </div> | 3051 | data-ng-show="canDeleteCacheSet(cacheset)" |
1374 | 2958 | </td> | 3052 | data-ng-click="toggleMenu(); quickCacheSetDelete(cacheset)">Remove…</button> |
1375 | 2959 | <td class="p-table-expanding__panel col-12" data-ng-if="cacheset.$selected && cachesetsMode === 'delete'"> | 3053 | </div> |
1338 | 2960 | <div class="row" data-ng-if="windowWidth <= 768"> | ||
1339 | 2961 | <div class="col-8"> | ||
1340 | 2962 | <h2 data-ng-click="filesystemCancel()" class="p-heading--four"> | ||
1341 | 2963 | <span data-ng-if="cachesetsMode === 'delete'">Removing {$ cacheset.name $}</span> | ||
1342 | 2964 | </h2> | ||
1376 | 2965 | </div> | 3054 | </div> |
1377 | 2966 | </div> | ||
1378 | 2967 | <div class="row" data-ng-class="{ 'is-active': cachesetsMode !== null && cachesetsMode !== 'multi' }"> | ||
1379 | 2968 | <div data-ng-show="cachesetsMode === 'single' && canDeleteCacheSet(cacheset)"> | ||
1380 | 2969 | <button class="p-button--base" | ||
1381 | 2970 | data-ng-show="canDeleteCacheSet(cacheset)" | ||
1382 | 2971 | data-ng-click="cacheSetDelete()">Remove</button> | ||
1383 | 2972 | </div> | 3055 | </div> |
1385 | 2973 | <div class="row ng-hide" data-ng-show="cachesetsMode === 'delete'"> | 3056 | </td> |
1386 | 3057 | <td class="p-table-expanding__panel col-12" data-ng-if="cacheset.$selected && cachesetsMode === 'delete'"> | ||
1387 | 3058 | <div class="row" data-ng-if="windowWidth <= 768"> | ||
1388 | 2974 | <div class="col-8"> | 3059 | <div class="col-8"> |
1390 | 2975 | <p><span class="p-icon--warning">Warning:</span> Are you sure you want to delete this cache set?</p> | 3060 | <h2 data-ng-click="filesystemCancel()" class="p-heading--four"> |
1391 | 3061 | <span data-ng-if="cachesetsMode === 'delete'">Removing {$ cacheset.name $}</span> | ||
1392 | 3062 | </h2> | ||
1393 | 3063 | </div> | ||
1394 | 3064 | </div> | ||
1395 | 3065 | <div class="row" data-ng-class="{ 'is-active': cachesetsMode !== null && cachesetsMode !== 'multi' }"> | ||
1396 | 3066 | <div data-ng-show="cachesetsMode === 'single' && canDeleteCacheSet(cacheset)"> | ||
1397 | 3067 | <button class="p-button--base" | ||
1398 | 3068 | data-ng-show="canDeleteCacheSet(cacheset)" | ||
1399 | 3069 | data-ng-click="cacheSetDelete()">Remove</button> | ||
1400 | 2976 | </div> | 3070 | </div> |
1405 | 2977 | <div class="col-4"> | 3071 | <div class="row ng-hide" data-ng-show="cachesetsMode === 'delete'"> |
1406 | 2978 | <div class="u-align--right"> | 3072 | <div class="col-8"> |
1407 | 2979 | <button class="p-button--base" type="button" data-ng-click="cacheSetCancel()">Cancel</button> | 3073 | <p><span class="p-icon--warning">Warning:</span> Are you sure you want to delete this cache set?</p> |
1408 | 2980 | <button class="p-button--negative u-no-margin--top" data-ng-click="cacheSetConfirmDelete(cacheset)">Remove cache set</button> | 3074 | </div> |
1409 | 3075 | <div class="col-4"> | ||
1410 | 3076 | <div class="u-align--right"> | ||
1411 | 3077 | <button class="p-button--base" type="button" data-ng-click="cacheSetCancel()">Cancel</button> | ||
1412 | 3078 | <button class="p-button--negative u-no-margin--top" data-ng-click="cacheSetConfirmDelete(cacheset)">Remove cache set</button> | ||
1413 | 3079 | </div> | ||
1414 | 2981 | </div> | 3080 | </div> |
1415 | 2982 | </div> | 3081 | </div> |
1416 | 2983 | </div> | 3082 | </div> |
1422 | 2984 | </div> | 3083 | </td> |
1423 | 2985 | </td> | 3084 | </tr> |
1424 | 2986 | </tr> | 3085 | </tbody> |
1425 | 2987 | </tbody> | 3086 | </table> |
1426 | 2988 | </table> | 3087 | </div> |
1427 | 3088 | </div> | ||
1428 | 3089 | <div> | ||
1429 | 3090 | <storage-disks-partitions></storage-disks-partitions> | ||
1430 | 2989 | </div> | 3091 | </div> |
1431 | 2990 | </div> | ||
1432 | 2991 | <div> | ||
1433 | 2992 | <storage-disks-partitions></storage-disks-partitions> | ||
1434 | 2993 | </div> | 3092 | </div> |
1435 | 2994 | </div> | 3093 | </div> |
1436 | 2995 | </form> | 3094 | </form> |
1437 | diff --git a/src/maasserver/static/partials/nodedetails/storage/datastores.html b/src/maasserver/static/partials/nodedetails/storage/datastores.html | |||
1438 | 2996 | new file mode 100644 | 3095 | new file mode 100644 |
1439 | index 0000000..28984c8 | |||
1440 | --- /dev/null | |||
1441 | +++ b/src/maasserver/static/partials/nodedetails/storage/datastores.html | |||
1442 | @@ -0,0 +1,112 @@ | |||
1443 | 1 | <div class="p-strip is-shallow"> | ||
1444 | 2 | <h3 class="p-heading--four">Datastores</h3> | ||
1445 | 3 | |||
1446 | 4 | <table class="p-table-expanding p-table--datastores col-12" role="grid"> | ||
1447 | 5 | <thead> | ||
1448 | 6 | <tr class="p-table__row"> | ||
1449 | 7 | <th scope="col" aria-sort="none" class="p-table__cell">Name</th> | ||
1450 | 8 | <th scope="col" aria-sort="none" class="p-table__cell">Filesystem</th> | ||
1451 | 9 | <th scope="col" aria-sort="none" class="p-table__cell">Size</th> | ||
1452 | 10 | <th scope="col" aria-sort="none" class="p-table__cell">Mount point</th> | ||
1453 | 11 | <th scope="col" aria-sort="none" class="p-table__cell u-align--right">Actions</th> | ||
1454 | 12 | <th class="u-hide"> | ||
1455 | 13 | <!-- empty cell for validation --> | ||
1456 | 14 | </th> | ||
1457 | 15 | </tr> | ||
1458 | 16 | </thead> | ||
1459 | 17 | <tbody> | ||
1460 | 18 | <tr data-ng-hide="node.disks.length" class="col-12"> | ||
1461 | 19 | <td> | ||
1462 | 20 | No datastores defined. | ||
1463 | 21 | </td> | ||
1464 | 22 | </tr> | ||
1465 | 23 | <tr class="p-table__row" data-ng-repeat="filesystem in node.disks" data-ng-class="{ 'is-active': filesystem.$selected }" data-ng-if="filesystem.used_for == 'VMFS Datastore'"> | ||
1466 | 24 | <td role="gridcell" class="p-table__cell" aria-label="Name" title="{$ filesystem.name $}">{$ filesystem.name $}</td> | ||
1467 | 25 | <td role="gridcell" class="p-table__cell" aria-label="Filesystem" title="VMFS6">VMFS6</td> | ||
1468 | 26 | <td role="gridcell" class="p-table__cell" aria-label="Size" title="{$ filesystem.size_human $}">{$ filesystem.size_human $}</td> | ||
1469 | 27 | <td role="gridcell" class="p-table__cell" aria-label="Mount point" title="{$ filesystem.path $}">{$ filesystem.path $}</td> | ||
1470 | 28 | <td role="gridcell" class="p-table__cell p-table--action-cell u-align--right"> | ||
1471 | 29 | <div class="p-contextual-menu" toggle-ctrl data-ng-if="!isAllStorageDisabled()"> | ||
1472 | 30 | <button class="p-button--base p-contextual-menu__toggle" aria-controls="#{$ item.name $}-menu" | ||
1473 | 31 | data-ng-click="toggleMenu()" aria-haspopup="true"> | ||
1474 | 32 | <i class="p-icon--contextual-menu">Actions</i> | ||
1475 | 33 | </button> | ||
1476 | 34 | <div class="p-contextual-menu__dropdown" role="menu" data-ng-show="isToggled" id="{$ item.name $}-menu"> | ||
1477 | 35 | <button class="p-contextual-menu__link" aria-label="Remove" | ||
1478 | 36 | data-ng-click="toggleMenu(); quickFilesystemDelete(filesystem)" | ||
1479 | 37 | data-ng-show="!isAllStorageDisabled() && filesystemMode !== 'delete'">Remove…</button> | ||
1480 | 38 | </div> | ||
1481 | 39 | </div> | ||
1482 | 40 | </td> | ||
1483 | 41 | <td class="p-table-expanding__panel--bordered" | ||
1484 | 42 | data-ng-if="filesystem.$selected && filesystemMode === 'delete'" | ||
1485 | 43 | aria-hidden="!filesystem.$selected && filesystemMode !== 'delete'"> | ||
1486 | 44 | <div class="row u-flex--no-wrap" data-ng-if="windowWidth <= 768"> | ||
1487 | 45 | <h2 data-ng-click="filesystemCancel()" class="p-heading--four"> | ||
1488 | 46 | <span data-ng-if="filesystemMode === 'delete'">Removing {$ filesystem.name $}</span> | ||
1489 | 47 | </h2> | ||
1490 | 48 | <button class="p-button--close" data-ng-click="filesystemCancel()"><span | ||
1491 | 49 | class="p-icon--close">Cancel</span></button> | ||
1492 | 50 | </div> | ||
1493 | 51 | <div data-ng-if="filesystemMode !== null && filesystemMode !== 'multi'" | ||
1494 | 52 | data-ng-class="{ 'is-active': filesystemMode !== null && filesystemMode !== 'multi' }"> | ||
1495 | 53 | <div data-ng-if="filesystemMode === 'delete'" class="p-space-between"> | ||
1496 | 54 | <p><span class="p-icon--warning">Warning:</span> Are you sure you want to remove this {$ | ||
1497 | 55 | getRemoveTypeText(filesystem) $}?</p> | ||
1498 | 56 | <div class="p-space-between__align-right"> | ||
1499 | 57 | <button class="p-button--base u-width--auto" type="button" | ||
1500 | 58 | data-ng-click="filesystemCancel(filesystem)">Cancel</button> | ||
1501 | 59 | <button class="p-button--negative u-width--auto" | ||
1502 | 60 | data-ng-click="filesystemConfirmDelete(filesystem)">Remove</button> | ||
1503 | 61 | </div> | ||
1504 | 62 | </div> | ||
1505 | 63 | </div> | ||
1506 | 64 | </td> | ||
1507 | 65 | </tr> | ||
1508 | 66 | |||
1509 | 67 | <tr class="is-active p-table__row" data-ng-if="dropdown" data-ng-switch="dropdown"> | ||
1510 | 68 | <!-- Adding a new TMPFS or RAMFPS filesystem --> | ||
1511 | 69 | <td class="p-table-expanding__panel" data-ng-controller="NodeAddSpecialFilesystemController" | ||
1512 | 70 | data-ng-switch-when="special"> | ||
1513 | 71 | <maas-obj-form obj="newFilesystem" manager="machineManager" manager-method="mountSpecialFilesystem" | ||
1514 | 72 | inline="false" save-on-blur="false" after-save="cancel"> | ||
1515 | 73 | <div class="row" data-ng-if="windowWidth <= 768"> | ||
1516 | 74 | <div class="u-flex--no-wrap"> | ||
1517 | 75 | <h2 data-ng-click="cancel()" class="u-align--left p-heading--four">Adding filesystem</h2> | ||
1518 | 76 | <button class="p-button--close" data-ng-click="cancel()" type="button"> | ||
1519 | 77 | <i class="p-icon--close">Cancel</i></button> | ||
1520 | 78 | </div> | ||
1521 | 79 | </div> | ||
1522 | 80 | <div class="row p-form p-form--stacked"> | ||
1523 | 81 | <div class="col-6"> | ||
1524 | 82 | <div class="p-form__group"> | ||
1525 | 83 | <label class="p-form__label mobile-col-2">Description</label> | ||
1526 | 84 | <div class="p-form__control p-form__control--placeholder mobile-col-2"> | ||
1527 | 85 | <span data-ng-bind="description"></span> | ||
1528 | 86 | </div> | ||
1529 | 87 | </div> | ||
1530 | 88 | <maas-obj-field type="options" key="fstype" label="Filesystem" subtle="false" | ||
1531 | 89 | options="type for type in specialFilesystemTypes"></maas-obj-field> | ||
1532 | 90 | </div> | ||
1533 | 91 | <div class="col-6"> | ||
1534 | 92 | <maas-obj-field type="text" key="mount_point" label="Mount point" subtle="false" | ||
1535 | 93 | placeholder="Absolute path"></maas-obj-field> | ||
1536 | 94 | <maas-obj-field type="text" key="mount_options" label="Mount options" subtle="false" | ||
1537 | 95 | placeholder="Separated by commas, no spaces"></maas-obj-field> | ||
1538 | 96 | </div> | ||
1539 | 97 | </div> | ||
1540 | 98 | <hr> | ||
1541 | 99 | <div class="p-space-between"> | ||
1542 | 100 | <maas-obj-errors></maas-obj-errors> | ||
1543 | 101 | <div class="p-space-between__align-right"> | ||
1544 | 102 | <button class="p-button--base u-width--auto" type="button" data-ng-click="cancel()">Cancel</button> | ||
1545 | 103 | <button class="p-button--neutral u-width--auto ng-binding" data-ng-disabled="!canMount()" | ||
1546 | 104 | maas-obj-save>Mount</button> | ||
1547 | 105 | </div> | ||
1548 | 106 | </div> | ||
1549 | 107 | </maas-obj-form> | ||
1550 | 108 | </td> | ||
1551 | 109 | </tr> | ||
1552 | 110 | </tbody> | ||
1553 | 111 | </table> | ||
1554 | 112 | </div> | ||
1555 | 0 | \ No newline at end of file | 113 | \ No newline at end of file |
1556 | diff --git a/src/maasserver/static/partials/nodedetails/storage/disks-partitions.html b/src/maasserver/static/partials/nodedetails/storage/disks-partitions.html | |||
1557 | index 8f85fc7..041f4ae 100644 | |||
1558 | --- a/src/maasserver/static/partials/nodedetails/storage/disks-partitions.html | |||
1559 | +++ b/src/maasserver/static/partials/nodedetails/storage/disks-partitions.html | |||
1560 | @@ -4,27 +4,25 @@ | |||
1561 | 4 | <table class="p-table-expanding p-table--disks-partitions col-12"> | 4 | <table class="p-table-expanding p-table--disks-partitions col-12"> |
1562 | 5 | <thead> | 5 | <thead> |
1563 | 6 | <tr class="p-table__row"> | 6 | <tr class="p-table__row"> |
1577 | 7 | <th class="col-3"> | 7 | <th class="p-double-row p-table__cell"> |
1578 | 8 | <div class="u-float--left"> | 8 | <div class="p-double-row__checkbox"> </div> |
1579 | 9 | <input type="checkbox" class="checkbox u-float--left" id="available-check-all" data-ng-hide="isAvailableDisabled()" data-ng-checked="availableAllSelected" | 9 | <div class="p-double-row__rows-container--checkbox"> |
1580 | 10 | data-ng-click="toggleAvailableAllSelect()" data-ng-disabled="isAvailableDisabled()" /> | 10 | <div>Name</div> |
1581 | 11 | <label for="available-check-all"></label> | 11 | <div>Serial</div> |
1582 | 12 | </div> | 12 | </div> |
1583 | 13 | <a data-ng-click="tableInfo.column = 'name'" data-ng-class="{'p-link--soft': tableInfo.column === 'name'}">Name</a> | 13 | </th> |
1584 | 14 | <span class="divide"> | </span> | 14 | <th class="p-double-row p-table__cell"> |
1585 | 15 | <a data-ng-click="tableInfo.column = 'model'" data-ng-class="{'p-link--soft': tableInfo.column === 'model'}">Model</a> | 15 | <div>Model</div> |
1586 | 16 | <span class="divide"> | </span> | 16 | <div>Firmware</div> |
1574 | 17 | <a data-ng-click="tableInfo.column = 'serial'" data-ng-class="{'p-link--soft': tableInfo.column === 'serial'}">Serial</a> | ||
1575 | 18 | <span class="divide"> | </span> | ||
1576 | 19 | <a data-ng-click="tableInfo.column = 'firmware_version'" data-ng-class="{'p-link--soft': tableInfo.column === 'firmware_version'}">Firmware</a> | ||
1587 | 20 | </th> | 17 | </th> |
1595 | 21 | <th class="col-1"><div class="u-align--center">Boot</div></th> | 18 | <th class="p-table__cell"><div class="u-align--center">Boot</div></th> |
1596 | 22 | <th class="col-1">Size</th> | 19 | <th class="p-table__cell">Size</th> |
1597 | 23 | <th class="col-1">Type</th> | 20 | <th class="col-3 p-double-row p-table__cell"> |
1598 | 24 | <th class="col-2">Filesystem</th> | 21 | <div>Type</div> |
1599 | 25 | <th class="col-2">Tags</th> | 22 | <div>Tags</div> |
1600 | 26 | <th class="col-1">Health</th> | 23 | </th> |
1601 | 27 | <th class="col-1"><div class="u-align--right">Actions</div></th> | 24 | <th class="p-table__cell">Health</th> |
1602 | 25 | <th class="p-table__cell"><div class="u-align--right">Actions</div></th> | ||
1603 | 28 | </tr> | 26 | </tr> |
1604 | 29 | </thead> | 27 | </thead> |
1605 | 30 | <tbody> | 28 | <tbody> |
1606 | @@ -34,32 +32,34 @@ | |||
1607 | 34 | </td> | 32 | </td> |
1608 | 35 | </tr> | 33 | </tr> |
1609 | 36 | <tr class="p-table__row" data-ng-repeat="item in available | removeAvailableByNew:availableNew" | 34 | <tr class="p-table__row" data-ng-repeat="item in available | removeAvailableByNew:availableNew" |
1612 | 37 | data-ng-class="{ 'is-active': item.$selected }"> | 35 | data-ng-class="{ 'is-active': item.$selected }" data-ng-if="item.parent_type !== 'vmfs6'"> |
1613 | 38 | <td class="col-3 p-form-validation" aria-label="Name" | 36 | <td class="p-form-validation p-double-row p-table__cell" aria-label="Name" |
1614 | 39 | data-ng-class="{ 'is-error': isNameInvalid(item) }"> | 37 | data-ng-class="{ 'is-error': isNameInvalid(item) }"> |
1635 | 40 | <div class="u-float--left"> | 38 | <div class="p-double-row__checkbox"> |
1636 | 41 | <input type="checkbox" class="checkbox u-float--left" id="{$ item.name $}" | 39 | <input type="checkbox" class="checkbox u-float--left" id="{$ item.name $}" data-ng-hide="isAvailableDisabled()" data-ng-checked="item.$selected" data-ng-click="toggleAvailableSelect(item)" data-ng-disabled="isAvailableDisabled()" /> |
1637 | 42 | data-ng-hide="isAvailableDisabled()" | 40 | <label for="{$ item.name $}"></label> |
1638 | 43 | data-ng-checked="item.$selected" | 41 | </div> |
1639 | 44 | data-ng-click="toggleAvailableSelect(item)" | 42 | <div class="p-double-row__rows-container--checkbox"> |
1640 | 45 | data-ng-disabled="isAvailableDisabled()" /> | 43 | <div class="p-double-row__main-row"> |
1641 | 46 | <label for="{$ item.name $}"> | 44 | {$ item.name $} |
1642 | 47 | <span data-ng-show="tableInfo.column === 'name'" | 45 | </div> |
1643 | 48 | data-ng-hide="availableMode === 'edit' && item.$selected" | 46 | <div class="p-double-row__muted-row"> |
1644 | 49 | title="{$ item.name $}">{$ item.name $}</span> | 47 | {$ item.serial $} |
1645 | 50 | </label> | 48 | </div> |
1646 | 51 | </div> | 49 | </div> |
1627 | 52 | <span data-ng-show="tableInfo.column === 'model'" title="{$ item.model $}">{$ item.model $}</span> | ||
1628 | 53 | <span data-ng-show="tableInfo.column === 'serial'" title="{$ item.serial $}">{$ item.serial $}</span> | ||
1629 | 54 | <span data-ng-show="tableInfo.column === 'firmware_version'" title="{$ item.firmware_version $}">{$ item.firmware_version $}</span> | ||
1630 | 55 | <input type="text" class="p-form-validation__input" | ||
1631 | 56 | data-ng-model="item.name" | ||
1632 | 57 | data-ng-show="availableMode === 'edit' && item.$selected" | ||
1633 | 58 | data-ng-disabled="item.type === 'partition' || isAllStorageDisabled() || !canEdit()" | ||
1634 | 59 | data-ng-change="nameHasChanged(item)"> | ||
1647 | 60 | </td> | 50 | </td> |
1650 | 61 | <td class="col-1" aria-label="Boot"> | 51 | <td class="p-double-row p-table__cell"> |
1651 | 62 | <div class="u-align--center"> | 52 | <div class="p-double-row__container"> |
1652 | 53 | <div class="p-double-row__main-row"> | ||
1653 | 54 | {$ item.model $} | ||
1654 | 55 | </div> | ||
1655 | 56 | <div class="p-double-row__muted-row"> | ||
1656 | 57 | {$ item.firmware_version $} | ||
1657 | 58 | </div> | ||
1658 | 59 | </div> | ||
1659 | 60 | </td> | ||
1660 | 61 | <td class="p-table__cell" aria-label="Boot"> | ||
1661 | 62 | <div class="u-align--center" data-ng-if="storageLayout.id !== 'vmfs6'"> | ||
1662 | 63 | <input type="radio" name="boot-disk" id="{$ item.name $}-boot" class="u-no-margin--right" | 63 | <input type="radio" name="boot-disk" id="{$ item.name $}-boot" class="u-no-margin--right" |
1663 | 64 | data-ng-click="setAsBootDisk(item)" | 64 | data-ng-click="setAsBootDisk(item)" |
1664 | 65 | data-ng-checked="item.is_boot" | 65 | data-ng-checked="item.is_boot" |
1665 | @@ -69,30 +69,31 @@ | |||
1666 | 69 | <label for="{$ item.name $}-boot" data-ng-hide="availableMode === 'edit' && item.$selected"></label> | 69 | <label for="{$ item.name $}-boot" data-ng-hide="availableMode === 'edit' && item.$selected"></label> |
1667 | 70 | </div> | 70 | </div> |
1668 | 71 | </td> | 71 | </td> |
1670 | 72 | <td class="col-1" aria-label="Size"> | 72 | <td class="p-table__cell" aria-label="Size"> |
1671 | 73 | <span data-ng-hide="availableMode === 'edit' && item.$selected" title="{$ item.size_human $}"> | 73 | <span data-ng-hide="availableMode === 'edit' && item.$selected" title="{$ item.size_human $}"> |
1673 | 74 | {$ item.size_human $} <span class="table__label ng-hide" data-ng-show="showFreeSpace(item)">Free: {$ item.available_size_human $}</span> | 74 | {$ item.size_human $} <span class="table__labeldatastore-name ng-hide" data-ng-show="showFreeSpace(item)">Free: {$ item.available_size_human $}</span> |
1674 | 75 | </span> | 75 | </span> |
1675 | 76 | </td> | 76 | </td> |
1686 | 77 | <td class="col-1" aria-label="type"> | 77 | <td class="p-double-row p-table__cell" aria-label="type"> |
1687 | 78 | <span data-ng-hide="availableMode === 'edit' && item.$selected" title="{$ getDeviceType(item) $}">{$ getDeviceType(item) $}</span> | 78 | <div class="p-double-rows__container"> |
1688 | 79 | </td> | 79 | <div class="p-double-row__main-row"> |
1689 | 80 | <td class="col-2" aria-label="Filesystem"> | 80 | {$ getDeviceType(item) $} |
1690 | 81 | <span data-ng-hide="availableMode === 'edit' && item.$selected" title="{$ item.fstype $}">{$ item.fstype $}</span> | 81 | </div> |
1691 | 82 | </td> | 82 | <div class="p-double-row__muted-row"> |
1692 | 83 | <td class="col-2" aria-label="Tags"> | 83 | <span class="table__tag" data-ng-repeat="tag in item.tags" data-ng-hide="item.$options.editingTags"> |
1693 | 84 | <span class="table__tag" data-ng-repeat="tag in item.tags" data-ng-hide="item.$options.editingTags"> | 84 | <a href="#/machines/?query=storage_tags:({$ tag.text $})" title="{$ tag.text $}">{$ tag.text $}</a> |
1694 | 85 | <a href="#/machines/?query=storage_tags:({$ tag.text $})" title="{$ tag.text $}">{$ tag.text $}</a> | 85 | </span> |
1695 | 86 | </span> | 86 | </div> |
1696 | 87 | </div> | ||
1697 | 87 | </td> | 88 | </td> |
1699 | 88 | <td class="col-1" aria-label="Health"> | 89 | <td class="p-table__cell" aria-label="Health"> |
1700 | 89 | <span data-maas-script-status="script-status" data-script-status="item.test_status" data-ng-if="item.type === 'physical'"></span> | 90 | <span data-maas-script-status="script-status" data-script-status="item.test_status" data-ng-if="item.type === 'physical'"></span> |
1701 | 90 | <span data-ng-if="item.test_status === 0 || item.test_status === 1 || item.test_status === 2 || item.test_status === 5 || item.test_status === 7" title="Ok">Ok</span> | 91 | <span data-ng-if="item.test_status === 0 || item.test_status === 1 || item.test_status === 2 || item.test_status === 5 || item.test_status === 7" title="Ok">Ok</span> |
1702 | 91 | <span data-ng-if="item.test_status === 3 || item.test_status === 4 || item.test_status === 8" title="Error">Error</span> | 92 | <span data-ng-if="item.test_status === 3 || item.test_status === 4 || item.test_status === 8" title="Error">Error</span> |
1703 | 92 | <span data-ng-if="item.test_status === 6" title="Degraded">Degraded</span> | 93 | <span data-ng-if="item.test_status === 6" title="Degraded">Degraded</span> |
1704 | 93 | <span data-ng-if="item.test_status === -1" title="Unknown">Unknown</span> | 94 | <span data-ng-if="item.test_status === -1" title="Unknown">Unknown</span> |
1705 | 94 | </td> | 95 | </td> |
1707 | 95 | <td class="col-1 p-table--action-cell"> | 96 | <td class="p-table--action-cell p-table__cell"> |
1708 | 96 | <div class="u-align--right"> | 97 | <div class="u-align--right"> |
1709 | 97 | <div class="p-contextual-menu" toggle-ctrl | 98 | <div class="p-contextual-menu" toggle-ctrl |
1710 | 98 | data-ng-if="canAddLogicalVolume(item) || canAddPartition(item) || canEdit(item) || canDelete(item)"> | 99 | data-ng-if="canAddLogicalVolume(item) || canAddPartition(item) || canEdit(item) || canDelete(item)"> |
1711 | @@ -463,7 +464,7 @@ | |||
1712 | 463 | <label class="checkbox-label" for="bcache-create"></label> | 464 | <label class="checkbox-label" for="bcache-create"></label> |
1713 | 464 | </div> | 465 | </div> |
1714 | 465 | <div class="u-float--left p-form-validation" | 466 | <div class="u-float--left p-form-validation" |
1716 | 466 | data-ng-class="{ 'is-error': isNewDiskNameInvalid() }"> | 467 | data-ng-class="{ 'is-error': isNewDiskNameInvalid(availableNew.name) }"> |
1717 | 467 | <input type="text" class="p-form-validation__input" | 468 | <input type="text" class="p-form-validation__input" |
1718 | 468 | data-ng-model="availableNew.name"> | 469 | data-ng-model="availableNew.name"> |
1719 | 469 | </div> | 470 | </div> |
1720 | @@ -481,7 +482,7 @@ | |||
1721 | 481 | <div class="row"> | 482 | <div class="row"> |
1722 | 482 | <div class="col-6"> | 483 | <div class="col-6"> |
1723 | 483 | <div class="p-form__group u-hide--large p-form-validation" | 484 | <div class="p-form__group u-hide--large p-form-validation" |
1725 | 484 | data-ng-class="{ 'is-error': isNewDiskNameInvalid() }"> | 485 | data-ng-class="{ 'is-error': isNewDiskNameInvalid(availableNew.name) }"> |
1726 | 485 | <label for="bcache-name" class="p-form__label">Name</label> | 486 | <label for="bcache-name" class="p-form__label">Name</label> |
1727 | 486 | <div class="p-form__control"> | 487 | <div class="p-form__control"> |
1728 | 487 | <input type="text" id="bcache-name" data-ng-model="availableNew.name" | 488 | <input type="text" id="bcache-name" data-ng-model="availableNew.name" |
1729 | @@ -600,7 +601,7 @@ | |||
1730 | 600 | <label for="raid-create"></label> | 601 | <label for="raid-create"></label> |
1731 | 601 | </div> | 602 | </div> |
1732 | 602 | <div class="u-float--left p-form-validation" | 603 | <div class="u-float--left p-form-validation" |
1734 | 603 | data-ng-class="{ 'is-error': isNewDiskNameInvalid() }"> | 604 | data-ng-class="{ 'is-error': isNewDiskNameInvalid(availableNew.name) }"> |
1735 | 604 | <input type="text" class="p-form-validation__input" | 605 | <input type="text" class="p-form-validation__input" |
1736 | 605 | data-ng-model="availableNew.name"> | 606 | data-ng-model="availableNew.name"> |
1737 | 606 | </div> | 607 | </div> |
1738 | @@ -619,7 +620,7 @@ | |||
1739 | 619 | <div class="col-6"> | 620 | <div class="col-6"> |
1740 | 620 | <div class="p-form__group u-hide--medium u-hide--large"> | 621 | <div class="p-form__group u-hide--medium u-hide--large"> |
1741 | 621 | <label for="new-raid-name" class="p-form__label">Name</label> | 622 | <label for="new-raid-name" class="p-form__label">Name</label> |
1743 | 622 | <div class="p-form__control" data-ng-class="{ 'is-error': isNewDiskNameInvalid() }"> | 623 | <div class="p-form__control" data-ng-class="{ 'is-error': isNewDiskNameInvalid(availableNew.name) }"> |
1744 | 623 | <input type="text" id="new-raid-name" data-ng-model="availableNew.name"> | 624 | <input type="text" id="new-raid-name" data-ng-model="availableNew.name"> |
1745 | 624 | </div> | 625 | </div> |
1746 | 625 | </div> | 626 | </div> |
1747 | @@ -734,7 +735,7 @@ | |||
1748 | 734 | <label for="vg-create"></label> | 735 | <label for="vg-create"></label> |
1749 | 735 | </div> | 736 | </div> |
1750 | 736 | <div class="u-float--left p-form-validation" | 737 | <div class="u-float--left p-form-validation" |
1752 | 737 | data-ng-class="{ 'is-error': isNewDiskNameInvalid() }"> | 738 | data-ng-class="{ 'is-error': isNewDiskNameInvalid(availableNew.name) }"> |
1753 | 738 | <input type="text" class="p-form-validation__input" | 739 | <input type="text" class="p-form-validation__input" |
1754 | 739 | data-ng-model="availableNew.name"> | 740 | data-ng-model="availableNew.name"> |
1755 | 740 | </div> | 741 | </div> |
1756 | @@ -753,7 +754,7 @@ | |||
1757 | 753 | <div class="col-6"> | 754 | <div class="col-6"> |
1758 | 754 | <div class="p-form__group p-form-validation"> | 755 | <div class="p-form__group p-form-validation"> |
1759 | 755 | <label for="new-vg-name" class="p-form__label">Name</label> | 756 | <label for="new-vg-name" class="p-form__label">Name</label> |
1761 | 756 | <div class="p-form__control" data-ng-class="{ 'is-error': isNewDiskNameInvalid() }"> | 757 | <div class="p-form__control" data-ng-class="{ 'is-error': isNewDiskNameInvalid(availableNew.name) }"> |
1762 | 757 | <input type="text" class="p-form-validation__input" id="new-vg-name" data-ng-model="availableNew.name"> | 758 | <input type="text" class="p-form-validation__input" id="new-vg-name" data-ng-model="availableNew.name"> |
1763 | 758 | </div> | 759 | </div> |
1764 | 759 | </div> | 760 | </div> |
1765 | @@ -806,57 +807,212 @@ | |||
1766 | 806 | </tr> | 807 | </tr> |
1767 | 807 | </tbody> | 808 | </tbody> |
1768 | 808 | </table> | 809 | </table> |
1798 | 809 | <button class="p-button--neutral p-tooltip p-tooltip--top-center" | 810 | |
1799 | 810 | data-ng-disabled="!canCreateRAID()" | 811 | <div class="p-card" data-ng-if="createNewDatastore"> |
1800 | 811 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | 812 | <div class="row"> |
1801 | 812 | data-ng-click="createRAID()"> | 813 | <div class="row p-form--stacked"> |
1802 | 813 | Create RAID | 814 | <div class="col-6"> |
1803 | 814 | <span class="p-tooltip__message" role="tooltip">Select two or more physical devices to create a RAID</span> | 815 | <div class="p-form__group p-form-validation" |
1804 | 815 | </button> | 816 | data-ng-class="{ 'is-error': isNewDiskNameInvalid(datastores.new.name) }"> |
1805 | 816 | <button class="p-button--neutral p-tooltip p-tooltip--top-center" | 817 | <label for="datastore-name" class="p-form__label u-sv3">Name</label> |
1806 | 817 | data-ng-disabled="!canCreateVolumeGroup()" | 818 | <div class="p-form__control"> |
1807 | 818 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | 819 | <input type="text" |
1808 | 819 | data-ng-click="createVolumeGroup()"> | 820 | name="datastore-name" |
1809 | 820 | Create volume group | 821 | id="datastore-name" |
1810 | 821 | <span class="p-tooltip__message" role="tooltip">Select one or more devices to create a volume group</span> | 822 | class="p-form-validation__input" |
1811 | 822 | </button> | 823 | data-ng-model="datastores.new.name" |
1812 | 823 | <button class="p-button--neutral p-tooltip p-tooltip--top-center" | 824 | data-ng-keydown="$event.keyCode == 13 && !isNewDiskNameInvalid(datastores.new.name) && $event.preventDefault()" |
1813 | 824 | data-ng-disabled="!canCreateCacheSet()" | 825 | data-ng-keyup="$event.keyCode == 13 && !isNewDiskNameInvalid(datastores.new.name) && createDatastore()"> |
1814 | 825 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | 826 | |
1815 | 826 | data-ng-click="createCacheSet()"> | 827 | <p class="p-form-validation__message" |
1816 | 827 | Create cache Set | 828 | data-ng-if="isNewDiskNameInvalid(datastores.new.name) && datastores.new.name != ''"> |
1817 | 828 | <span class="p-tooltip__message" role="tooltip">Select one device to create a cache set</span> | 829 | <strong>Error:</strong> Disk name is already in use |
1818 | 829 | </button> | 830 | </p> |
1819 | 830 | <button class="p-button--neutral p-tooltip--top-center" | 831 | <p class="p-form-validation__message" |
1820 | 831 | data-ng-class="{ 'p-tooltip': !canCreateBcache() }" | 832 | data-ng-if="isNewDiskNameInvalid(datastores.new.name) && datastores.new.name == ''"> |
1821 | 832 | data-ng-disabled="!canCreateBcache()" | 833 | <strong>Error:</strong> Please enter a name for your new datastore |
1822 | 833 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | 834 | </p> |
1823 | 834 | data-ng-click="createBcache()"> | 835 | </div> |
1824 | 835 | Create bcache | 836 | </div> |
1825 | 836 | <span class="p-tooltip__message" role="tooltip">{$ getCannotCreateBcacheMsg() $}</span> | 837 | </div> |
1826 | 837 | </button> | 838 | <div class="col-6"> |
1827 | 839 | <div class="p-form__group"> | ||
1828 | 840 | <label for="datastore-filesystem" class="p-form__label u-sv3">Filesystem</label> | ||
1829 | 841 | <div class="p-form__control"> | ||
1830 | 842 | <p>{$ datastores.new.filesystem $}</p> | ||
1831 | 843 | </div> | ||
1832 | 844 | </div> | ||
1833 | 845 | <div class="p-form__group" data-ng-if="datastores.new.path"> | ||
1834 | 846 | <label for="datastore-mountpoint" class="p-form__label">Mount point</label> | ||
1835 | 847 | <div class="p-form__control"> | ||
1836 | 848 | <p>{$ datastores.new.path $}</p> | ||
1837 | 849 | </div> | ||
1838 | 850 | </div> | ||
1839 | 851 | </div> | ||
1840 | 852 | </div> | ||
1841 | 853 | <div class="u-sv2"> | ||
1842 | 854 | <hr /> | ||
1843 | 855 | </div> | ||
1844 | 856 | <div class="u-align--right"> | ||
1845 | 857 | <button class="p-button--neutral u-no-margin--bottom" data-ng-click="closeNewDatastorePanel()" data-ng-disabled="creatingDatastore">Cancel</button> | ||
1846 | 858 | <button class="p-button--positive u-no-margin--bottom" data-ng-click="createDatastore()" data-ng-if="!creatingDatastore" data-ng-disabled="isNewDiskNameInvalid(datastores.new.name)"> | ||
1847 | 859 | Create datastore | ||
1848 | 860 | </button> | ||
1849 | 861 | <button class="p-button--positive u-no-margin--bottom" data-ng-if="creatingDatastore"> | ||
1850 | 862 | <i class="p-icon--spinner is-light u-animation--spin"></i> | ||
1851 | 863 | | ||
1852 | 864 | Creating datastore | ||
1853 | 865 | </button> | ||
1854 | 866 | </div> | ||
1855 | 867 | </div> | ||
1856 | 868 | </div> | ||
1857 | 869 | <div class="p-card" data-ng-if="addToExistingDatastore"> | ||
1858 | 870 | <div class="row p-form--stacked"> | ||
1859 | 871 | <div class="col-6"> | ||
1860 | 872 | <div class="p-form__group"> | ||
1861 | 873 | <label for="datastore-name" class="p-form__label u-sv3">Datastore</label> | ||
1862 | 874 | <div class="p-form__control"> | ||
1863 | 875 | <select name="datastore-name" id="datastore-name" | ||
1864 | 876 | data-ng-model="datastores.old" | ||
1865 | 877 | data-ng-options="disk as disk.name for disk in node.disks | datastoresOnly"> | ||
1866 | 878 | </select> | ||
1867 | 879 | </div> | ||
1868 | 880 | </div> | ||
1869 | 881 | </div> | ||
1870 | 882 | <div class="col-6"> | ||
1871 | 883 | <div class="p-form__group"> | ||
1872 | 884 | <label for="datastore-mountpoint" class="p-form__label">Mount point</label> | ||
1873 | 885 | <div class="p-form__control"> | ||
1874 | 886 | <p>{$ datastores.old.path $}</p> | ||
1875 | 887 | </div> | ||
1876 | 888 | </div> | ||
1877 | 889 | </div> | ||
1878 | 890 | </div> | ||
1879 | 891 | <div class="u-sv2"> | ||
1880 | 892 | <hr /> | ||
1881 | 893 | </div> | ||
1882 | 894 | <div class="u-align--right"> | ||
1883 | 895 | <button class="p-button--neutral u-no-margin--bottom" data-ng-click="closeAddToExistingDatastorePanel()" data-ng-disabled="updatingDatastore">Cancel</button> | ||
1884 | 896 | <span class="p-tooltip p-tooltip--top-right"> | ||
1885 | 897 | <button class="p-button--positive u-no-margin--bottom" | ||
1886 | 898 | data-ng-click="addToDatastore()" | ||
1887 | 899 | data-ng-if="!updatingDatastore" | ||
1888 | 900 | data-ng-disabled="!addToDatastoreValid"> | ||
1889 | 901 | Add to datastore | ||
1890 | 902 | </button> | ||
1891 | 903 | <span class="p-tooltip__message" role="tooltip" data-ng-if="!addToDatastoreValid">Disks with partitions cannot be added to a datastore</span> | ||
1892 | 904 | </span> | ||
1893 | 905 | <button class="p-button--positive u-no-margin--bottom" data-ng-if="updatingDatastore"> | ||
1894 | 906 | <i class="p-icon--spinner is-light u-animation--spin"></i> | ||
1895 | 907 | | ||
1896 | 908 | Adding to datastore | ||
1897 | 909 | </button> | ||
1898 | 910 | </div> | ||
1899 | 911 | </div> | ||
1900 | 912 | |||
1901 | 913 | <div> | ||
1902 | 914 | <span class="p-tooltip p-tooltip--top-left"> | ||
1903 | 915 | <button class="p-button--neutral" | ||
1904 | 916 | data-ng-disabled="!canCreateRAID() || storageLayout.id === 'vmfs6'" | ||
1905 | 917 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | ||
1906 | 918 | data-ng-click="createRAID()"> | ||
1907 | 919 | Create RAID | ||
1908 | 920 | </button> | ||
1909 | 921 | <span data-ng-if="!canCreateRAID()"> | ||
1910 | 922 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id !== 'vmfs6'">Select two or more physical devices to create a RAID</span> | ||
1911 | 923 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id === 'vmfs6'">Not supported in the VMFS6 layout</span> | ||
1912 | 924 | </span> | ||
1913 | 925 | </span> | ||
1914 | 926 | <span class="p-tooltip p-tooltip--top-center"> | ||
1915 | 927 | <button class="p-button--neutral" | ||
1916 | 928 | data-ng-disabled="!canCreateVolumeGroup() || storageLayout.id === 'vmfs6'" | ||
1917 | 929 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | ||
1918 | 930 | data-ng-click="createVolumeGroup()"> | ||
1919 | 931 | Create volume group | ||
1920 | 932 | </button> | ||
1921 | 933 | <span data-ng-if="!canCreateVolumeGroup()"> | ||
1922 | 934 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id !== 'vmfs6'">Select one or more devices to create a volume group</span> | ||
1923 | 935 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id === 'vmfs6'">Not supported in the VMFS6 layout</span> | ||
1924 | 936 | </span> | ||
1925 | 937 | </span> | ||
1926 | 938 | <span class="p-tooltip p-tooltip--top-center"> | ||
1927 | 939 | <button class="p-button--neutral" | ||
1928 | 940 | data-ng-disabled="!canCreateCacheSet() || storageLayout.id === 'vmfs6'" | ||
1929 | 941 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | ||
1930 | 942 | data-ng-click="createCacheSet()"> | ||
1931 | 943 | Create cache Set | ||
1932 | 944 | </button> | ||
1933 | 945 | <span data-ng-if="!canCreateCacheSet()"> | ||
1934 | 946 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id !== 'vmfs6'">Select one device to create a cache set</span> | ||
1935 | 947 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id === 'vmfs6'">Not supported in the VMFS6 layout</span> | ||
1936 | 948 | </span> | ||
1937 | 949 | </span> | ||
1938 | 950 | <span class="p-tooltip p-tooltip--top-center"> | ||
1939 | 951 | <button class="p-button--neutral" | ||
1940 | 952 | data-ng-class="{ 'p-tooltip': !canCreateBcache() }" | ||
1941 | 953 | data-ng-disabled="!canCreateBcache() || storageLayout.id === 'vmfs6'" | ||
1942 | 954 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | ||
1943 | 955 | data-ng-click="createBcache()"> | ||
1944 | 956 | Create bcache | ||
1945 | 957 | </button> | ||
1946 | 958 | <span data-ng-if="!canCreateBcache()"> | ||
1947 | 959 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id !== 'vmfs6'">{$ getCannotCreateBcacheMsg() $}</span> | ||
1948 | 960 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id === 'vmfs6'">Not supported in the VMFS6 layout</span> | ||
1949 | 961 | </span> | ||
1950 | 962 | </span> | ||
1951 | 963 | <span class="p-tooltip p-tooltip--top-center"> | ||
1952 | 964 | <button class="p-button--neutral" | ||
1953 | 965 | data-ng-click="openNewDatastorePanel()" | ||
1954 | 966 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | ||
1955 | 967 | data-ng-disabled="!canPerformActionOnDatastoreSet()"> | ||
1956 | 968 | Create new datastore | ||
1957 | 969 | </button> | ||
1958 | 970 | <span data-ng-if="!canPerformActionOnDatastoreSet()"> | ||
1959 | 971 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id === 'vmfs6'">Select one or more devices to create a datastore</span> | ||
1960 | 972 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id !== 'vmfs6'">Only available in the VMFS6 layout</span> | ||
1961 | 973 | </span> | ||
1962 | 974 | </span> | ||
1963 | 975 | <span class="p-tooltip p-tooltip--top-center" data-ng-if="storageLayout.id === 'vmfs6'"> | ||
1964 | 976 | <button class="p-button--neutral" | ||
1965 | 977 | data-ng-click="openAddToExistingDatastorePanel()" | ||
1966 | 978 | data-ng-hide="isAllStorageDisabled() || !canEdit()" | ||
1967 | 979 | data-ng-disabled="!canPerformActionOnDatastoreSet()"> | ||
1968 | 980 | Add to existing datastore | ||
1969 | 981 | </button> | ||
1970 | 982 | <span data-ng-if="!canPerformActionOnDatastoreSet()"> | ||
1971 | 983 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id === 'vmfs6'">Select one or more devices to add an existing datastore</span> | ||
1972 | 984 | <span class="p-tooltip__message" role="tooltip" data-ng-if="storageLayout.id !== 'vmfs6'">Only available in the VMFS6 layout</span> | ||
1973 | 985 | </span> | ||
1974 | 986 | </span> | ||
1975 | 987 | </div> | ||
1976 | 838 | </div> | 988 | </div> |
1977 | 839 | </div> | 989 | </div> |
1978 | 990 | |||
1979 | 840 | <div class="p-strip is-shallow"> | 991 | <div class="p-strip is-shallow"> |
1980 | 841 | <div class="row"> | 992 | <div class="row"> |
1981 | 842 | <hr> | ||
1982 | 843 | <h3 class="p-heading--four">Used disks and partitions</h3> | 993 | <h3 class="p-heading--four">Used disks and partitions</h3> |
1984 | 844 | <table class="p-table-expanding"> | 994 | <table class="p-table-expanding p-table--used-disks"> |
1985 | 845 | <thead> | 995 | <thead> |
1995 | 846 | <tr> | 996 | <tr class="p-table__row"> |
1996 | 847 | <th class="col-3"> | 997 | <th class="p-double-row p-table__cell"> |
1997 | 848 | <a data-ng-click="tableInfo.column = 'name'" data-ng-class="{'p-link--soft': tableInfo.column === 'name'}">Name</a> | 998 | <div>Name</div> |
1998 | 849 | <span class="divide"> | </span> | 999 | <div>Serial</div> |
1999 | 850 | <a data-ng-click="tableInfo.column = 'model'" data-ng-class="{'p-link--soft': tableInfo.column === 'model'}">Model</a> | 1000 | </th> |
2000 | 851 | <span class="divide"> | </span> | 1001 | <th class="p-double-row p-table__cell"> |
2001 | 852 | <a data-ng-click="tableInfo.column = 'serial'" data-ng-class="{'p-link--soft': tableInfo.column === 'serial'}">Serial</a> | 1002 | <div>Model</div> |
2002 | 853 | <span class="divide"> | </span> | 1003 | <div>Firmware</div> |
2003 | 854 | <a data-ng-click="tableInfo.column = 'firmware_version'" data-ng-class="{'p-link--soft': tableInfo.column === 'firmware_version'}">Firmware</a> | 1004 | </th> |
2004 | 1005 | <th class="p-table__cell"><div class="u-align--center">Boot</div></th> | ||
2005 | 1006 | <th class="p-table__cell">Size</th> | ||
2006 | 1007 | <th class="p-double-row p-table__cell"> | ||
2007 | 1008 | <div>Type</div> | ||
2008 | 1009 | <div>Tags</div> | ||
2009 | 1010 | </th> | ||
2010 | 1011 | <th class="p-table__cell">Health</th> | ||
2011 | 1012 | <th class="p-double-row p-table__cell"> | ||
2012 | 1013 | <div>Used for</div> | ||
2013 | 1014 | <div>Mount point</div> | ||
2014 | 855 | </th> | 1015 | </th> |
2015 | 856 | <th class="col-1"><div class="u-align--center">Boot</div></th> | ||
2016 | 857 | <th class="col-2">Device type</th> | ||
2017 | 858 | <th class="col-3">Used for</th> | ||
2018 | 859 | <th class="col-2">Health</th> | ||
2019 | 860 | </tr> | 1016 | </tr> |
2020 | 861 | </thead> | 1017 | </thead> |
2021 | 862 | <tbody> | 1018 | <tbody> |
2022 | @@ -865,16 +1021,31 @@ | |||
2023 | 865 | No disk or partition has been fully utilized. | 1021 | No disk or partition has been fully utilized. |
2024 | 866 | </td> | 1022 | </td> |
2025 | 867 | </tr> | 1023 | </tr> |
2032 | 868 | <tr data-ng-repeat="item in used" class="table__row details__used"> | 1024 | <tr data-ng-repeat="item in used" class="table__row details__used p-table__row"> |
2033 | 869 | <td class="col-3" aria-label="Name"> | 1025 | <td class="p-double-row p-table__cell" aria-label="Name"> |
2034 | 870 | <span data-ng-show="tableInfo.column === 'name'" title="{$ item.name $}">{$ item.name $}</span> | 1026 | <div class="p-double-rows__container"> |
2035 | 871 | <span data-ng-show="tableInfo.column === 'model'" title="{$ item.model $}">{$ item.model $}</span> | 1027 | <div class="p-double-row__main-row"> |
2036 | 872 | <span data-ng-show="tableInfo.column === 'serial'" title="{$ item.serial $}">{$ item.serial $}</span> | 1028 | {$ item.name $} |
2037 | 873 | <span data-ng-show="tableInfo.column === 'firmware_version'" title="{$ item.firmware_version $}">{$ item.firmware_version $}</span> | 1029 | </div> |
2038 | 1030 | <div class="p-double-row__muted-row"> | ||
2039 | 1031 | {$ item.serial $} | ||
2040 | 1032 | </div> | ||
2041 | 1033 | </div> | ||
2042 | 1034 | </td> | ||
2043 | 1035 | <td class="p-double-row p-table__cell"> | ||
2044 | 1036 | <div class="p-double-rows__container"> | ||
2045 | 1037 | <div class="p-double-row__main-row"> | ||
2046 | 1038 | {$ item.model $} | ||
2047 | 1039 | </div> | ||
2048 | 1040 | <div class="p-double-row__muted-row"> | ||
2049 | 1041 | {$ item.firmware_version $} | ||
2050 | 1042 | </div> | ||
2051 | 1043 | </div> | ||
2052 | 874 | </td> | 1044 | </td> |
2055 | 875 | <td class="col-1" aria-label="Boot disk"> | 1045 | <td aria-label="Boot disk" class="p-table__cell"> |
2056 | 876 | <div class="u-align--center"> | 1046 | <div class="u-align--center" data-ng-hide="item.parent_type !== 'vmfs6' && !item.is_boot"> |
2057 | 877 | <input type="radio" id="{$ item.name $}-boot" name="boot-disk" | 1047 | <input type="radio" id="{$ item.name $}-boot" name="boot-disk" |
2058 | 1048 | class="u-no-margin--right" | ||
2059 | 878 | data-ng-click="setAsBootDisk(item)" | 1049 | data-ng-click="setAsBootDisk(item)" |
2060 | 879 | data-ng-checked="item.is_boot" | 1050 | data-ng-checked="item.is_boot" |
2061 | 880 | data-ng-if="item.type === 'physical'" | 1051 | data-ng-if="item.type === 'physical'" |
2062 | @@ -882,19 +1053,46 @@ | |||
2063 | 882 | <label for="{$ item.name $}-boot"></label> | 1053 | <label for="{$ item.name $}-boot"></label> |
2064 | 883 | </div> | 1054 | </div> |
2065 | 884 | </td> | 1055 | </td> |
2069 | 885 | <td class="col-2" aria-label="Device type" title="{$ getDeviceType(item) $}">{$ getDeviceType(item) $}</td> | 1056 | <td class="p-table__cell">{$ item.size_human $}</td> |
2070 | 886 | <td class="col-3" aria-label="Used for" title="{$ item.used_for $}">{$ item.used_for $}</td> | 1057 | <td class="p-double-row p-table__cell"> |
2071 | 887 | <td class="col-2" aria-label="Health"> | 1058 | <div class="p-double-rows__container"> |
2072 | 1059 | <div class="p-double-row__main-row"> | ||
2073 | 1060 | {$ getDeviceType(item) $} | ||
2074 | 1061 | </div> | ||
2075 | 1062 | <div class="p-double-row__muted-row"> | ||
2076 | 1063 | <span class="table__tag" data-ng-repeat="tag in item.tags" data-ng-hide="item.$options.editingTags"> | ||
2077 | 1064 | <a href="#/machines/?query=storage_tags:({$ tag.text $})" title="{$ tag.text $}">{$ tag.text $}</a> | ||
2078 | 1065 | </span> | ||
2079 | 1066 | </div> | ||
2080 | 1067 | </div> | ||
2081 | 1068 | </td> | ||
2082 | 1069 | <td aria-label="Health" class="p-table__cell"> | ||
2083 | 888 | <span data-ng-if="item.type === 'physical'"> | 1070 | <span data-ng-if="item.type === 'physical'"> |
2084 | 889 | <span data-maas-script-status="script-status" data-script-status="item.test_status"></span> | 1071 | <span data-maas-script-status="script-status" data-script-status="item.test_status"></span> |
2087 | 890 | <span data-ng-if="item.test_status === 0 || item.test_status === 1 || item.test_status === 2 || item.test_status === 5 || item.test_status === 7" title="Ok">Ok</span> | 1072 | <span |
2088 | 891 | <span data-ng-if="item.test_status === 3 || item.test_status === 4 || item.test_status === 8" title="Error">Error</span> | 1073 | data-ng-if="item.test_status === 0 || item.test_status === 1 || item.test_status === 2 || item.test_status === 5 || item.test_status === 7" |
2089 | 1074 | title="Ok">Ok</span> | ||
2090 | 1075 | <span data-ng-if="item.test_status === 3 || item.test_status === 4 || item.test_status === 8" | ||
2091 | 1076 | title="Error">Error</span> | ||
2092 | 892 | <span data-ng-if="item.test_status === 6" title="Degraded">Degraded</span> | 1077 | <span data-ng-if="item.test_status === 6" title="Degraded">Degraded</span> |
2093 | 893 | <span data-ng-if="item.test_status === -1" title="Unknown">Unknown</span> | 1078 | <span data-ng-if="item.test_status === -1" title="Unknown">Unknown</span> |
2094 | 894 | </span> | 1079 | </span> |
2095 | 895 | </td> | 1080 | </td> |
2096 | 1081 | <td class="p-double-row p-table__cell" aria-label="Used for" title="{$ item.used_for $}"> | ||
2097 | 1082 | <div class="p-double-rows__container"> | ||
2098 | 1083 | <div class="p-double-row__main-row"> | ||
2099 | 1084 | {$ item.used_for $} | ||
2100 | 1085 | </div> | ||
2101 | 1086 | <div class="p-double-row__muted-row"> | ||
2102 | 1087 | {$ item.mount_point $} | ||
2103 | 1088 | </div> | ||
2104 | 1089 | </div> | ||
2105 | 1090 | </td> | ||
2106 | 896 | </tr> | 1091 | </tr> |
2107 | 897 | </tbody> | 1092 | </tbody> |
2108 | 898 | </table> | 1093 | </table> |
2109 | 899 | </div> | 1094 | </div> |
2110 | 900 | </div> | 1095 | </div> |
2111 | 1096 | |||
2112 | 1097 | <p>Learn more about deploying <a href="https://docs.maas.io/en/installconfig-images" class="p-link--external" target="_blank">Windows</a></p> | ||
2113 | 1098 | <p>Change the default layout in <a href="/MAAS/settings/storage/">Settings › Storage</a></p> | ||
2114 | diff --git a/src/maasserver/static/partials/nodedetails/storage/filesystems.html b/src/maasserver/static/partials/nodedetails/storage/filesystems.html | |||
2115 | index 340739e..908e7aa 100644 | |||
2116 | --- a/src/maasserver/static/partials/nodedetails/storage/filesystems.html | |||
2117 | +++ b/src/maasserver/static/partials/nodedetails/storage/filesystems.html | |||
2118 | @@ -110,7 +110,7 @@ | |||
2119 | 110 | </tr> | 110 | </tr> |
2120 | 111 | </tbody> | 111 | </tbody> |
2121 | 112 | </table> | 112 | </table> |
2123 | 113 | <button class="p-button--neutral p-tooltip--top-center" data-ng-disabled="dropdown !== null" data-ng-class="{ 'p-tooltip': dropdown === null}" | 113 | <button class="p-button--neutral p-tooltip--top-left" data-ng-disabled="dropdown !== null" data-ng-class="{ 'p-tooltip': dropdown === null}" |
2124 | 114 | data-ng-if="!isAllStorageDisabled()" data-ng-click="addSpecialFilesystem()"> | 114 | data-ng-if="!isAllStorageDisabled()" data-ng-click="addSpecialFilesystem()"> |
2125 | 115 | Add special filesystem | 115 | Add special filesystem |
2126 | 116 | <span class="p-tooltip__message" role="tooltip">Create a tmpfs or ramfs filesystem</span> | 116 | <span class="p-tooltip__message" role="tooltip">Create a tmpfs or ramfs filesystem</span> |
2127 | diff --git a/src/maasserver/static/scss/_base_tables.scss b/src/maasserver/static/scss/_base_tables.scss | |||
2128 | index 710e416..bfdd68e 100644 | |||
2129 | --- a/src/maasserver/static/scss/_base_tables.scss | |||
2130 | +++ b/src/maasserver/static/scss/_base_tables.scss | |||
2131 | @@ -40,7 +40,6 @@ | |||
2132 | 40 | flex-basis: auto !important; | 40 | flex-basis: auto !important; |
2133 | 41 | flex-grow: 0; | 41 | flex-grow: 0; |
2134 | 42 | vertical-align: top; | 42 | vertical-align: top; |
2135 | 43 | padding-bottom: 0.05rem; | ||
2136 | 44 | 43 | ||
2137 | 45 | &:first-of-type { | 44 | &:first-of-type { |
2138 | 46 | padding-left: $sph-intra--condensed; | 45 | padding-left: $sph-intra--condensed; |
2139 | diff --git a/src/maasserver/static/scss/_patterns_notification.scss b/src/maasserver/static/scss/_patterns_notification.scss | |||
2140 | index ab3dab2..65620d4 100644 | |||
2141 | --- a/src/maasserver/static/scss/_patterns_notification.scss | |||
2142 | +++ b/src/maasserver/static/scss/_patterns_notification.scss | |||
2143 | @@ -1,6 +1,7 @@ | |||
2144 | 1 | @mixin maas-p-notifications { | 1 | @mixin maas-p-notifications { |
2145 | 2 | @include maas-notification; | 2 | @include maas-notification; |
2146 | 3 | @include maas-notification-group; | 3 | @include maas-notification-group; |
2147 | 4 | @include maas-notification-subtle; | ||
2148 | 4 | } | 5 | } |
2149 | 5 | 6 | ||
2150 | 6 | @mixin maas-notification-group { | 7 | @mixin maas-notification-group { |
2151 | @@ -23,3 +24,14 @@ | |||
2152 | 23 | } | 24 | } |
2153 | 24 | } | 25 | } |
2154 | 25 | } | 26 | } |
2155 | 27 | |||
2156 | 28 | @mixin maas-notification-subtle { | ||
2157 | 29 | [class*="p-notification"].is-subtle { | ||
2158 | 30 | background: transparent; | ||
2159 | 31 | box-shadow: none; | ||
2160 | 32 | |||
2161 | 33 | &::before { | ||
2162 | 34 | height: 0; | ||
2163 | 35 | } | ||
2164 | 36 | } | ||
2165 | 37 | } | ||
2166 | diff --git a/src/maasserver/static/scss/_tables.scss b/src/maasserver/static/scss/_tables.scss | |||
2167 | index 47738fd..6194abb 100644 | |||
2168 | --- a/src/maasserver/static/scss/_tables.scss | |||
2169 | +++ b/src/maasserver/static/scss/_tables.scss | |||
2170 | @@ -326,9 +326,12 @@ | |||
2171 | 326 | } | 326 | } |
2172 | 327 | } | 327 | } |
2173 | 328 | 328 | ||
2175 | 329 | .p-table--disks-partitions { | 329 | .p-table--disks-partitions, |
2176 | 330 | .p-table--used-disks { | ||
2177 | 330 | .p-table__row { | 331 | .p-table__row { |
2178 | 331 | .p-table__cell { | 332 | .p-table__cell { |
2179 | 333 | flex: 0 0 auto !important; | ||
2180 | 334 | |||
2181 | 332 | &:nth-child(1) { | 335 | &:nth-child(1) { |
2182 | 333 | width: 15%; | 336 | width: 15%; |
2183 | 334 | } | 337 | } |
2184 | @@ -346,22 +349,58 @@ | |||
2185 | 346 | } | 349 | } |
2186 | 347 | 350 | ||
2187 | 348 | &:nth-child(5) { | 351 | &:nth-child(5) { |
2189 | 349 | width: 12%; | 352 | width: 22%; |
2190 | 350 | } | 353 | } |
2191 | 351 | 354 | ||
2192 | 352 | &:nth-child(6) { | 355 | &:nth-child(6) { |
2193 | 356 | width: 22%; | ||
2194 | 357 | } | ||
2195 | 358 | |||
2196 | 359 | &:nth-child(7) { | ||
2197 | 353 | width: 10%; | 360 | width: 10%; |
2198 | 354 | } | 361 | } |
2199 | 362 | } | ||
2200 | 363 | } | ||
2201 | 364 | } | ||
2202 | 365 | |||
2203 | 366 | .p-table--used-disks { | ||
2204 | 367 | .p-table__row { | ||
2205 | 368 | .p-table__cell { | ||
2206 | 369 | &:nth-child(6) { | ||
2207 | 370 | width: 7%; | ||
2208 | 371 | } | ||
2209 | 355 | 372 | ||
2210 | 356 | &:nth-child(7) { | 373 | &:nth-child(7) { |
2212 | 357 | width: 12%; | 374 | width: 25%; |
2213 | 358 | } | 375 | } |
2214 | 376 | } | ||
2215 | 377 | } | ||
2216 | 378 | } | ||
2217 | 359 | 379 | ||
2220 | 360 | &:nth-child(8) { | 380 | .p-table--datastores { |
2221 | 361 | width: 10%; | 381 | flex: 0 0 auto !important; |
2222 | 382 | |||
2223 | 383 | .p-table__row { | ||
2224 | 384 | .p-table__cell { | ||
2225 | 385 | flex: 0 0 auto !important; | ||
2226 | 386 | |||
2227 | 387 | &:nth-child(1) { | ||
2228 | 388 | width: 15%; | ||
2229 | 389 | } | ||
2230 | 390 | |||
2231 | 391 | &:nth-child(2) { | ||
2232 | 392 | width: 22%; | ||
2233 | 362 | } | 393 | } |
2234 | 363 | 394 | ||
2236 | 364 | &:nth-child(9) { | 395 | &:nth-child(3) { |
2237 | 396 | width: 9%; | ||
2238 | 397 | } | ||
2239 | 398 | |||
2240 | 399 | &:nth-child(4) { | ||
2241 | 400 | width: 44%; | ||
2242 | 401 | } | ||
2243 | 402 | |||
2244 | 403 | &:nth-child(5) { | ||
2245 | 365 | width: 10%; | 404 | width: 10%; |
2246 | 366 | } | 405 | } |
2247 | 367 | } | 406 | } |
2248 | diff --git a/src/maasserver/static/scss/_utils.scss b/src/maasserver/static/scss/_utils.scss | |||
2249 | index f6528d2..1724cf8 100644 | |||
2250 | --- a/src/maasserver/static/scss/_utils.scss | |||
2251 | +++ b/src/maasserver/static/scss/_utils.scss | |||
2252 | @@ -67,3 +67,7 @@ $table-h-indent: $sph-intra--condensed; | |||
2253 | 67 | .u-mirror--y { | 67 | .u-mirror--y { |
2254 | 68 | transform: rotate(180deg); | 68 | transform: rotate(180deg); |
2255 | 69 | } | 69 | } |
2256 | 70 | |||
2257 | 71 | .u-rotate { | ||
2258 | 72 | transform: rotate(180deg); | ||
2259 | 73 | } |
LGTM +1