]> git.datanom.net - omvzfs.git/blob - gui/js/omv/module/admin/storage/zfs/Overview.js
4731124a7016385e636441a1f111fefa79fae95f
[omvzfs.git] / gui / js / omv / module / admin / storage / zfs / Overview.js
1 // require("js/omv/tree/Panel.js")
2 // require("js/omv/module/admin/storage/zfs/TreePanel.js")
3 // require("js/omv/workspace/window/Grid.js")
4 // require("js/omv/form/field/CheckboxGrid.js")
5
6 Ext.define("OMV.module.admin.storage.zfs.ShowDetails", {
7 extend: "OMV.workspace.window.Form",
8 requires: [
9 "OMV.data.Store",
10 "OMV.data.Model",
11 "OMV.data.proxy.Rpc",
12 ],
13
14 rpcService: "ZFS",
15 title: _("Object details"),
16 autoLoadData: true,
17 hideResetButton: true,
18 hideCancelButton: true,
19 width: 550,
20 height: 350,
21 layout: 'fit',
22 okButtonText: _("Ok"),
23
24 getFormItems: function() {
25 var me = this;
26
27 return [{
28 xtype: "textareafield",
29 name: "details",
30 grow: true,
31 anchor: '100%',
32 readOnly: true
33 }];
34
35 }
36 });
37
38 Ext.define("OMV.module.admin.storage.zfs.AddPool", {
39 extend: "OMV.workspace.window.Form",
40 requires: [
41 "OMV.data.Store",
42 "OMV.data.Model",
43 "OMV.data.proxy.Rpc",
44 "OMV.form.field.CheckboxGrid"
45 ],
46
47 rpcService: "ZFS",
48 rpcSetMethod: "addPool",
49 title: _("Create ZFS pool"),
50 autoLoadData: false,
51 hideResetButton: true,
52 width: 550,
53 height: 350,
54
55 getFormItems: function() {
56 var me = this;
57 return [{
58 xtype: "textfield",
59 name: "name",
60 fieldLabel: _("Name")
61 },{
62 xtype: "combo",
63 name: "pooltype",
64 fieldLabel: _("Pool type"),
65 queryMode: "local",
66 store: Ext.create("Ext.data.ArrayStore", {
67 fields: [ "value", "text" ],
68 data: [
69 [ "basic", _("Basic") ],
70 [ "mirror", _("Mirror") ],
71 [ "raidz1", _("RAID-Z1") ],
72 [ "raidz2", _("RAID-Z2") ],
73 [ "raidz3", _("RAID-Z3") ]
74 ]
75 }),
76 displayField: "text",
77 valueField: "value",
78 allowBlank: false,
79 editable: false,
80 triggerAction: "all",
81 value: "raidz1",
82 listeners: {
83 scope: me,
84 change: function(combo, value) {
85 var devicesField = this.findField("devices");
86 switch(value) {
87 case "basic":
88 devicesField.minSelections = 1;
89 break;
90 case "mirror":
91 devicesField.minSelections = 2;
92 break;
93 case "raidz1":
94 devicesField.minSelections = 3;
95 break;
96 case "raidz2":
97 devicesField.minSelections = 4;
98 break;
99 case "raidz3":
100 devicesField.minSelections = 5;
101 break;
102 default:
103 devicesField.minSelections = 2;
104 break;
105 }
106 devicesField.validate();
107 }
108 }
109 },{
110 xtype: "checkboxgridfield",
111 name: "devices",
112 fieldLabel: _("Devices"),
113 valueField: "devicefile",
114 minSelections: 3, // Min. number of devices for RAIDZ-1
115 useStringValue: true,
116 height: 130,
117 store: Ext.create("OMV.data.Store", {
118 autoLoad: true,
119 model: OMV.data.Model.createImplicit({
120 idProperty: "devicefile",
121 fields: [
122 { name: "devicefile", type: "string" },
123 { name: "size", type: "string" },
124 { name: "vendor", type: "string" },
125 { name: "serialnumber", type: "string" }
126 ]
127 }),
128 proxy: {
129 type: "rpc",
130 appendSortParams: false,
131 rpcData: {
132 service: "RaidMgmt",
133 method: "getCandidates"
134 }
135 },
136 sorters: [{
137 direction: "ASC",
138 property: "devicefile"
139 }]
140 }),
141 gridConfig: {
142 stateful: true,
143 stateId: "1866b5d0-327e-11e4-8c21-0800200c9a66",
144 columns: [{
145 text: _("Device"),
146 sortable: true,
147 dataIndex: "devicefile",
148 stateId: "devicefile",
149 flex: 1
150 },{
151 xtype: "binaryunitcolumn",
152 text: _("Capacity"),
153 sortable: true,
154 dataIndex: "size",
155 stateId: "size",
156 width: 50,
157 flex: 1
158 },{
159 text: _("Vendor"),
160 sortable: true,
161 dataIndex: "vendor",
162 stateId: "vendor",
163 flex: 1
164 },{
165 text: _("Serial Number"),
166 sortable: true,
167 dataIndex: "serialnumber",
168 stateId: "serialnumber",
169 flex: 1
170 }]
171 }
172 },{
173 xtype: "textfield",
174 name: "mountpoint",
175 fieldLabel: _("Mountpoint"),
176 plugins: [{
177 ptype: "fieldinfo",
178 text: _("Optional mountpoint for the pool. Default is to use pool name.")
179 }]
180 },{
181 xtype: "checkbox",
182 name: "force",
183 fieldLabel: _("Force creation"),
184 checked: false,
185 plugins: [{
186 ptype: "fieldinfo",
187 text: _("Forces the creation of the pool even if errors are reported. Use with extreme caution!")
188 }]
189 }];
190 },
191
192 doSubmit: function() {
193 var me = this;
194 OMV.MessageBox.show({
195 title: _("Confirmation"),
196 msg: _("Do you really want to create the ZFS pool?"),
197 buttons: Ext.Msg.YESNO,
198 fn: function(answer) {
199 if(answer === "no")
200 return;
201 me.superclass.doSubmit.call(me);
202 },
203 scope: me,
204 icon: Ext.Msg.QUESTION
205 });
206 }
207 });
208
209 Ext.define("OMV.module.admin.storage.zfs.AddObject", {
210 extend: "OMV.workspace.window.Form",
211 uses: [
212 "OMV.data.Store",
213 "OMV.data.Model",
214 "OMV.data.proxy.Rpc",
215 "OMV.data.reader.RpcArray"
216 ],
217
218 rpcService: "ZFS",
219 rpcSetMethod: "addObject",
220 width: 420,
221
222 getFormItems: function() {
223 var me = this;
224 return [{
225 xtype: "combo",
226 name: "type",
227 fieldLabel: _("Object Type"),
228 queryMode: "local",
229 store: [
230 [ "filesystem", "Filesystem" ],
231 [ "snapshot", "Snapshot" ],
232 [ "volume", "Volume" ]
233 ],
234 allowBlank: true,
235 editable: false,
236 triggerAction: "all",
237 value: "filesystem",
238 listeners: {
239 scope: me,
240 change: function(combo, value) {
241 var sizeField = this.findField("size");
242 switch(value) {
243 case "volume":
244 sizeField.show();
245 sizeField.allowBlank = false;
246 break;
247 default:
248 sizeField.hide();
249 sizeField.allowBlank = true;
250 break;
251 }
252 sizeField.validate();
253 }
254 }
255 },{
256 xtype: "textfield",
257 name: "path",
258 fieldLabel: _("Prefix"),
259 allowBlank: false,
260 readOnly: true,
261 value: me.path
262 },{
263 xtype: "textfield",
264 name: "name",
265 fieldLabel: _("Name"),
266 allowBlank: false,
267 plugins: [{
268 ptype: "fieldinfo",
269 text: _("Name of the new object. Prefix will prepend the name. Please omit leading /")
270 }]
271 },{
272 xtype: "textfield",
273 name: "size",
274 hidden: true,
275 fieldLabel: _("Size"),
276 allowBlank: true,
277 plugins: [{
278 ptype: "fieldinfo",
279 text: _("Size of the volume e.g. 5mb,100gb,1tb etc")
280 }]
281 }];
282 }
283 });
284
285 Ext.define("OMV.module.admin.storage.zfs.ExpandPool", {
286 extend: "OMV.workspace.window.Form",
287 uses: [
288 "OMV.data.Store",
289 "OMV.data.Model",
290 "OMV.data.proxy.Rpc",
291 "OMV.data.reader.RpcArray"
292 ],
293
294 rpcService: "ZFS",
295 rpcSetMethod: "expandPool",
296 width: 550,
297 height: 350,
298 autoLoadData: true,
299
300 getFormItems: function() {
301 var me = this;
302 return [{
303 xtype: "textfield",
304 name: "name",
305 fieldLabel: _("Name"),
306 allowBlank: false,
307 readOnly: true,
308 value: me.name
309 },{
310 xtype: "combo",
311 name: "vdevtype",
312 fieldLabel: _("Vdev type"),
313 queryMode: "local",
314 store: Ext.create("Ext.data.ArrayStore", {
315 fields: [ "value", "text" ],
316 data: [
317 [ "basic", _("Basic") ],
318 [ "mirror", _("Mirror") ],
319 [ "raidz1", _("RAID-Z1") ],
320 [ "raidz2", _("RAID-Z2") ],
321 [ "raidz3", _("RAID-Z3") ]
322 ]
323 }),
324 displayField: "text",
325 valueField: "value",
326 allowBlank: false,
327 editable: false,
328 triggerAction: "all",
329 value: "raidz1",
330 listeners: {
331 scope: me,
332 change: function(combo, value) {
333 var devicesField = this.findField("devices");
334 switch(value) {
335 case "basic":
336 devicesField.minSelections = 1;
337 break;
338 case "mirror":
339 devicesField.minSelections = 2;
340 break;
341 case "raidz1":
342 devicesField.minSelections = 3;
343 break;
344 case "raidz2":
345 devicesField.minSelections = 4;
346 break;
347 case "raidz3":
348 devicesField.minSelections = 5;
349 break;
350 default:
351 devicesField.minSelections = 2;
352 break;
353 }
354 devicesField.validate();
355 }
356 }
357 },{
358 xtype: "checkboxgridfield",
359 name: "devices",
360 fieldLabel: _("Devices"),
361 valueField: "devicefile",
362 minSelections: 3, // Min. number of devices for RAIDZ-1
363 useStringValue: true,
364 height: 130,
365 store: Ext.create("OMV.data.Store", {
366 autoLoad: true,
367 model: OMV.data.Model.createImplicit({
368 idProperty: "devicefile",
369 fields: [
370 { name: "devicefile", type: "string" },
371 { name: "size", type: "string" },
372 { name: "vendor", type: "string" },
373 { name: "serialnumber", type: "string" }
374 ]
375 }),
376 proxy: {
377 type: "rpc",
378 appendSortParams: false,
379 rpcData: {
380 service: "RaidMgmt",
381 method: "getCandidates"
382 }
383 },
384 sorters: [{
385 direction: "ASC",
386 property: "devicefile"
387 }]
388 }),
389 gridConfig: {
390 stateful: true,
391 stateId: "05c60750-5074-11e4-916c-0800200c9a66",
392 columns: [{
393 text: _("Device"),
394 sortable: true,
395 dataIndex: "devicefile",
396 stateId: "devicefile",
397 flex: 1
398 },{
399 xtype: "binaryunitcolumn",
400 text: _("Capacity"),
401 sortable: true,
402 dataIndex: "size",
403 stateId: "size",
404 width: 50,
405 flex: 1
406 },{
407 text: _("Vendor"),
408 sortable: true,
409 dataIndex: "vendor",
410 stateId: "vendor",
411 flex: 1
412 },{
413 text: _("Serial Number"),
414 sortable: true,
415 dataIndex: "serialnumber",
416 stateId: "serialnumber",
417 flex: 1
418 }]
419 }
420 },{
421 xtype: "checkbox",
422 name: "force",
423 fieldLabel: _("Force creation"),
424 checked: false,
425 plugins: [{
426 ptype: "fieldinfo",
427 text: _("Forces the creation of the Vdev even if errors are reported. Use with extreme caution!")
428 }]
429 }];
430 }
431 });
432
433
434 Ext.define("OMV.module.admin.storage.zfs.EditProperties", {
435 extend: "OMV.workspace.window.Grid",
436 requires: [
437 "OMV.data.Store",
438 "OMV.data.Model",
439 "OMV.data.proxy.Rpc"
440 ],
441
442 rpcService: "ZFS",
443 rpcSetMethod: "setProperties",
444
445 title: _("Edit properties"),
446 width: 500,
447 height: 305,
448
449 getGridConfig: function() {
450 var me = this;
451
452 var rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
453 clicksToEdit: 1,
454 pluginId: 'rowEditing',
455 listeners: {
456 validateedit: function(editor, e, eOpts) {
457 e.record.set("modified", "true");
458 },
459 beforeedit: function(editor, e, eOpts) {
460 if (e.record.get("newproperty") === "false") {
461 e.grid.getPlugin('rowEditing').editor.form.findField("value").enable();
462 e.grid.getPlugin('rowEditing').editor.form.findField("property").disable();
463 } else {
464 e.grid.getPlugin('rowEditing').editor.form.findField("value").enable();
465 e.grid.getPlugin('rowEditing').editor.form.findField("property").enable();
466 }
467 }
468
469 }
470 });
471
472 var store = Ext.create("OMV.data.Store", {
473 autoLoad: true,
474 model: OMV.data.Model.createImplicit({
475 fields: [
476 { name: "property", type: "string" },
477 { name: "value", type: "string" },
478 { name: "source", type: "string" },
479 { name: "modified", type: "string" },
480 { name: "newproperty", type: "string", defaultValue: "false" }
481 ]
482 }),
483 proxy: {
484 type: "rpc",
485 rpcData: {
486 service: "ZFS",
487 method: "getProperties",
488 params: {
489 name: me.name,
490 type: me.type
491 }
492 }
493 }
494 });
495
496 return {
497 border: false,
498 stateful: true,
499 stateId: "8c3dc800-bdbb-11e3-b1b6-0800200c9a66",
500 selType: 'rowmodel',
501 plugins: [rowEditing],
502 store: store,
503 tbar: [{
504 text: "Add property",
505 icon: "images/add.png",
506 iconCls: Ext.baseCSSPrefix + "btn-icon-16x16",
507 handler: function(view) {
508 Ext.define('Property', {
509 extend: 'Ext.data.Model',
510 fields: [
511 "property",
512 "value",
513 "source",
514 "modified",
515 "newproperty"
516 ]
517 });
518 var newProperty = Ext.create("Property", {
519 property: "",
520 value: "",
521 source: "local",
522 modified: "true",
523 newproperty: "true"
524 });
525 rowEditing.cancelEdit();
526 store.insert(0, newProperty);
527 rowEditing.startEdit();
528 }
529 }],
530 columns: [{
531 text: _("Property"),
532 sortable: true,
533 dataIndex: "property",
534 stateId: "property",
535 editor: {
536 xtype: "textfield",
537 allowBlank: false,
538 }
539 },{
540 text: _("Value"),
541 sortable: true,
542 dataIndex: "value",
543 stateId: "value",
544 flex: 1,
545 readOnly: true,
546 editor: {
547 xtype: "textfield",
548 allowBlank: false,
549 }
550 },{
551 text: _("Source"),
552 sortable: true,
553 dataIndex: "source",
554 stateId: "source",
555 },{
556 xtype: 'actioncolumn',
557 header: 'Inherit',
558 icon: "images/checkmark.png",
559 tooltip: "Inherit",
560 handler: function(view, rowIndex, colIndex, item, e, record, row) {
561 OMV.RpcObserver.request({
562 msg : _("Updating property..."),
563 rpcData : {
564 service: "ZFS",
565 method: "inherit",
566 params: {
567 name: me.name,
568 type: me.type,
569 property: record.get("property")
570 }
571 },
572 finish : function() {
573 view.getStore().reload();
574 }
575 });
576 },
577 isDisabled: function(view, rowIdx, colIdx, item, record) {
578 var src = record.get("source");
579 if(src === "local") {
580 return false;
581 } else {
582 return true;
583 }
584 }
585 },{
586 text: _("New"),
587 dataIndex: "newproperty",
588 stateId: "newproperty",
589 sortable: false,
590 hidden: true
591 },{
592 text: _("Modified"),
593 sortable: false,
594 dataIndex: "modified",
595 stateId: "modified",
596 hidden: true
597 }],
598 };
599 },
600
601 getRpcSetParams: function() {
602 var me = this;
603 var properties = [];
604 var values = me.getValues();
605 Ext.Array.each(values, function(value) {
606 if(value.modified === "false")
607 return;
608 properties.push({
609 "property": value.property,
610 "value": value.value,
611 });
612 });
613 return {
614 name: me.name,
615 type: me.type,
616 properties: properties
617 };
618 }
619
620 });
621
622
623 Ext.define("OMV.module.admin.storage.zfs.CreateShare", {
624 extend: "OMV.workspace.window.Form",
625 uses: [
626 "OMV.data.Store",
627 "OMV.data.Model",
628 "OMV.data.proxy.Rpc",
629 "OMV.data.reader.RpcArray"
630 ],
631
632 rpcService: "ZFS",
633 rpcSetMethod: "createShare",
634 width: 500,
635
636 getFormItems: function() {
637 var me = this;
638 return [{
639 xtype: "textfield",
640 name: "sharename",
641 fieldLabel: _("Name"),
642 allowBlank: false,
643 },{
644 xtype: "textfield",
645 name: "mountpoint",
646 fieldLabel: _("Path"),
647 allowBlank: true,
648 readOnly: false
649 },{
650 xtype: "combo",
651 name: "mode",
652 fieldLabel: _("Permissions"),
653 queryMode: "local",
654 store: Ext.create("Ext.data.ArrayStore", {
655 fields: [ "value", "text" ],
656 data: [
657 [ "700", _("Administrator: read/write, Users: no access, Others: no access") ],
658 [ "750", _("Administrator: read/write, Users: read-only, Others: no access") ],
659 [ "770", _("Administrator: read/write, Users: read/write, Others: no access") ],
660 [ "755", _("Administrator: read/write, Users: read-only, Others: read-only") ],
661 [ "775", _("Administrator: read/write, Users: read/write, Others: read-only") ],
662 [ "777", _("Everyone: read/write") ]
663 ]
664 }),
665 displayField: "text",
666 valueField: "value",
667 allowBlank: false,
668 editable: false,
669 showItemTooltip: true,
670 triggerAction: "all",
671 value: "775",
672 plugins: [{
673 ptype: "fieldinfo",
674 text: _("The file mode of the shared folder path.")
675 }]
676 },{
677 xtype: "textarea",
678 name: "comment",
679 fieldLabel: _("Comment"),
680 allowBlank: true
681 },{
682 xtype: "textarea",
683 name: "name",
684 hidden: true
685 },{
686 xtype: "textarea",
687 name: "type",
688 hidden: true
689 }];
690 }
691 });
692
693
694
695 Ext.define("OMV.module.admin.storage.zfs.Overview", {
696 extend: "OMV.module.admin.storage.zfs.TreePanel",
697
698 rpcService: "ZFS",
699 rpcGetMethod: "getObjectTree",
700 requires: [
701 "OMV.data.Store",
702 "OMV.data.Model",
703 "OMV.data.proxy.Rpc"
704 ],
705
706 rootVisible: false,
707 stateful: true,
708 stateId: "cec54550-bc2a-11e3-a5e2-0800200c9a66",
709
710 columns: [{
711 text: _("Name"),
712 xtype: 'treecolumn',
713 dataIndex: 'name',
714 sortable: true,
715 flex: 2,
716 stateId: 'name'
717 },{
718 text: _("Type"),
719 dataIndex: 'type',
720 sortable: true,
721 flex: 1,
722 stateId: 'type',
723 renderer: function(value, p, r){
724 if (r.data['type'] == "Pool") {
725 return r.data['type'] + ' (' + r.data['pool_type'] + ')';
726 } else {
727 return r.data['type'];
728 }
729 }
730 },{
731 text: _("Size"),
732 dataIndex: 'size',
733 sortable: true,
734 flex: 1,
735 stateId: 'size'
736 },{
737 text: _("Used"),
738 dataIndex: 'used',
739 sortable: true,
740 flex: 1,
741 stateId: 'used'
742 },{
743 text: _("Available"),
744 dataIndex: 'available',
745 sortable: true,
746 flex: 1,
747 stateId: 'available'
748 },{
749 text: _("Mountpoint"),
750 dataIndex: 'mountpoint',
751 sortable: true,
752 flex: 1,
753 stateId: 'mountpoint'
754 },{
755 text: _("Share"),
756 xtype: 'actioncolumn',
757 tooltip: 'Create shared folder',
758 align: 'center',
759 icon: 'images/checkmark.png',
760 handler: function(view, rowIndex, colIndex, item, e, record, row) {
761 var me = this;
762 Ext.create("OMV.module.admin.storage.zfs.CreateShare", {
763 title: _("Create shared folder"),
764 rpcGetMethod: "getSharedParams",
765 rpcGetParams: {
766 name: record.get('path'),
767 type: record.get('type')
768 }
769 }).show();
770 },
771 isDisabled: function(view, rowIdx, colIdx, item, record) {
772 var src = record.get("type");
773 if((src === "Filesystem") && (record.get("shared") === "false")) {
774 return false;
775 } else {
776 return true;
777 }
778 }
779 },{
780 text: _("Details"),
781 xtype: 'actioncolumn',
782 tooltip: 'Details',
783 align: 'center',
784 icon: 'images/search.png',
785 handler: function(view, rowIndex, colIndex, item, e, record, row) {
786 var me = this;
787 Ext.create("OMV.module.admin.storage.zfs.ShowDetails", {
788 title: _("Object details"),
789 rpcGetMethod: "getObjectDetails",
790 rpcGetParams: {
791 name: record.get('path'),
792 type: record.get('type')
793 }
794 }).show();
795 }
796 },{
797 text: _("Shared"),
798 dataIndex: 'shared',
799 sortable: false,
800 stateId: 'shared',
801 hidden: true
802 }],
803
804 initComponent: function() {
805 var me = this;
806 this.width = 600;
807 Ext.apply(me, {
808 store: Ext.create("Ext.data.TreeStore", {
809 autoLoad: true,
810 model: OMV.data.Model.createImplicit({
811 fields: [
812 { name: "name", type: "string" },
813 { name: "type", type: "string" },
814 { name: "size", type: "string" },
815 { name: "used", type: "string" },
816 { name: "available", type: "string" },
817 { name: "mountpoint", type: "string" },
818 { name: "id", type: "string" },
819 { name: "path", type: "string" },
820 { name: "origin", type: "string", defaultValue: "none" },
821 { name: "shared", type: "string", defaultValue: "false" },
822 { name: "pool_type", type: "string"},
823 { name: "nr_disks", type: "string"}
824 ]
825 }),
826 proxy: {
827 type: "rpc",
828 rpcData: {
829 service: "ZFS",
830 method: "getObjectTree",
831 }
832 },
833 folderSort: true
834 })
835 });
836 me.callParent(arguments);
837 },
838
839 onAddButton: function() {
840 var me = this;
841 Ext.create("OMV.module.admin.storage.zfs.AddPool", {
842 listeners: {
843 scope: me,
844 submit: function() {
845 this.doReload();
846 }
847 }
848 }).show();
849 },
850
851 onAddObjButton: function() {
852 var me = this;
853 var sm = me.getSelectionModel();
854 var records = sm.getSelection();
855 var record = records[0];
856 Ext.create("OMV.module.admin.storage.zfs.AddObject", {
857 title: _("Add Object"),
858 path: record.get("path"),
859 listeners: {
860 scope: me,
861 submit: function() {
862 this.doReload();
863 }
864 }
865 }).show();
866 },
867
868 onEditButton: function() {
869 var me = this;
870 var sm = me.getSelectionModel();
871 var records = sm.getSelection();
872 var record = records[0];
873 Ext.create("OMV.module.admin.storage.zfs.EditProperties", {
874 name: record.get("path"),
875 type: record.get("type")
876 }).show();
877 },
878
879 onExpandPoolButton: function() {
880 var me = this;
881 var sm = me.getSelectionModel();
882 var records = sm.getSelection();
883 var record = records[0];
884 Ext.create("OMV.module.admin.storage.zfs.ExpandPool", {
885 title: _("Expand Pool"),
886 name: record.get("path"),
887 listeners: {
888 scope: me,
889 submit: function() {
890 this.doReload();
891 }
892 }
893 }).show();
894 },
895
896 doDeletion: function(record) {
897 var me = this;
898 OMV.Rpc.request({
899 scope: me,
900 callback: me.onDeletion,
901 rpcData: {
902 service: "ZFS",
903 method: "deleteObject",
904 params: {
905 name: record.get('path'),
906 type: record.get('type')
907 }
908 }
909 });
910 }
911
912 });
913
914 OMV.WorkspaceManager.registerPanel({
915 id: "overview",
916 path: "/storage/zfs",
917 text: _("Overview"),
918 position: 10,
919 className: "OMV.module.admin.storage.zfs.Overview"
920 });
921
922
923
This page took 0.154734 seconds and 4 git commands to generate.