From a6c3a4dd0c35379fcdec4462739dc92d758a882f Mon Sep 17 00:00:00 2001 From: Niclas Berglind Date: Wed, 8 Oct 2014 22:05:23 +0200 Subject: [PATCH] Added functionality to expand pool. Signed-off-by: Niclas Berglind --- .../omv/module/admin/storage/zfs/Overview.js | 141 +++++++++++++++++- .../omv/module/admin/storage/zfs/TreePanel.js | 33 +++- gui/rpc/zfs.inc | 50 +++++++ images/zfs_expand.png | Bin 0 -> 224 bytes src/Utils.php | 27 ++++ 5 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 images/zfs_expand.png diff --git a/gui/js/omv/module/admin/storage/zfs/Overview.js b/gui/js/omv/module/admin/storage/zfs/Overview.js index 2a3fe15..0bad639 100644 --- a/gui/js/omv/module/admin/storage/zfs/Overview.js +++ b/gui/js/omv/module/admin/storage/zfs/Overview.js @@ -280,6 +280,114 @@ Ext.define("OMV.module.admin.storage.zfs.AddObject", { } }); +Ext.define("OMV.module.admin.storage.zfs.ExpandPool", { + extend: "OMV.workspace.window.Form", + uses: [ + "OMV.data.Store", + "OMV.data.Model", + "OMV.data.proxy.Rpc", + "OMV.data.reader.RpcArray" + ], + + rpcService: "ZFS", + rpcSetMethod: "expandPool", + width: 550, + height: 350, + autoLoadData: true, + + getFormItems: function() { + var me = this; + return [{ + xtype: "textfield", + name: "name", + fieldLabel: _("Name"), + allowBlank: false, + readOnly: true, + value: me.name + },{ + xtype: "textfield", + name: "pool_type", + fieldLabel: _("Pool type"), + allowBlank: false, + readOnly: true, + value: me.pool_type + },{ + xtype: "checkboxgridfield", + name: "devices", + fieldLabel: _("Devices"), + valueField: "devicefile", + listeners: { + scope: me, + change: function(e, eOpts) { + var deviceField = this.findField("devices"); + if (me.pool_type == "Basic") { + deviceField.minSelections = 1; + } else { + deviceField.minSelections = me.nr_disks; + deviceField.maxSelections = me.nr_disks; + } + } + }, + useStringValue: true, + height: 130, + store: Ext.create("OMV.data.Store", { + autoLoad: true, + model: OMV.data.Model.createImplicit({ + idProperty: "devicefile", + fields: [ + { name: "devicefile", type: "string" }, + { name: "size", type: "string" }, + { name: "vendor", type: "string" }, + { name: "serialnumber", type: "string" } + ] + }), + proxy: { + type: "rpc", + appendSortParams: false, + rpcData: { + service: "RaidMgmt", + method: "getCandidates" + } + }, + sorters: [{ + direction: "ASC", + property: "devicefile" + }] + }), + gridConfig: { + stateful: true, + stateId: "04942d40-4ee3-11e4-916c-0800200c9a66", + columns: [{ + text: _("Device"), + sortable: true, + dataIndex: "devicefile", + stateId: "devicefile", + flex: 1 + },{ + xtype: "binaryunitcolumn", + text: _("Capacity"), + sortable: true, + dataIndex: "size", + stateId: "size", + width: 50, + flex: 1 + },{ + text: _("Vendor"), + sortable: true, + dataIndex: "vendor", + stateId: "vendor", + flex: 1 + },{ + text: _("Serial Number"), + sortable: true, + dataIndex: "serialnumber", + stateId: "serialnumber", + flex: 1 + }] + } + }]; + } +}); Ext.define("OMV.module.admin.storage.zfs.EditProperties", { @@ -570,7 +678,14 @@ Ext.define("OMV.module.admin.storage.zfs.Overview", { dataIndex: 'type', sortable: true, flex: 1, - stateId: 'type' + stateId: 'type', + renderer: function(value, p, r){ + if (r.data['type'] == "Pool") { + return r.data['type'] + ' (' + r.data['pool_type'] + ')'; + } else { + return r.data['type']; + } + } },{ text: _("Size"), dataIndex: 'size', @@ -662,7 +777,9 @@ Ext.define("OMV.module.admin.storage.zfs.Overview", { { name: "id", type: "string" }, { name: "path", type: "string" }, { name: "origin", type: "string", defaultValue: "none" }, - { name: "shared", type: "string", defaultValue: "false" } + { name: "shared", type: "string", defaultValue: "false" }, + { name: "pool_type", type: "string"}, + { name: "nr_disks", type: "string"} ] }), proxy: { @@ -721,6 +838,26 @@ Ext.define("OMV.module.admin.storage.zfs.Overview", { type: record.get("type") }).show(); }, + + onExpandPoolButton: function() { + var me = this; + var sm = me.getSelectionModel(); + var records = sm.getSelection(); + var record = records[0]; + Ext.create("OMV.module.admin.storage.zfs.ExpandPool", { + title: _("Expand Pool"), + name: record.get("path"), + type: record.get("type"), + pool_type: record.get("pool_type"), + nr_disks: record.get("nr_disks"), + listeners: { + scope: me, + submit: function() { + this.doReload(); + } + } + }).show(); + }, doDeletion: function(record) { var me = this; diff --git a/gui/js/omv/module/admin/storage/zfs/TreePanel.js b/gui/js/omv/module/admin/storage/zfs/TreePanel.js index 919b513..ab9c83f 100644 --- a/gui/js/omv/module/admin/storage/zfs/TreePanel.js +++ b/gui/js/omv/module/admin/storage/zfs/TreePanel.js @@ -105,8 +105,10 @@ Ext.define("OMV.module.admin.storage.zfs.TreePanel", { hideDownButton: true, hideApplyButton: true, hideRefreshButton: true, + hideExpandPoolButton: true, addButtonText: _("Add Pool"), addObjButtonText: _("Add Object"), + expandPoolButtonText: _("Expand"), editButtonText: _("Edit"), deleteButtonText: _("Delete"), upButtonText: _("Up"), @@ -213,6 +215,16 @@ Ext.define("OMV.module.admin.storage.zfs.TreePanel", { handler: Ext.Function.bind(me.onAddObjButton, me, [ me ]), scope: me, disabled: true + },{ + id: me.getId() + "-expand", + xtype: "button", + text: me.expandPoolButtonText, + icon: "images/zfs_expand.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + hidden: me.hideExpandPoolButton, + handler: Ext.Function.bind(me.onExpandPoolButton, me, [ me ]), + scope: me, + disabled: true },{ id: me.getId() + "-delete", xtype: "button", @@ -273,18 +285,20 @@ Ext.define("OMV.module.admin.storage.zfs.TreePanel", { var me = this; if(me.hideTopToolbar) return; - var tbarBtnName = [ "addobj", "edit", "delete", "up", "down" ]; + var tbarBtnName = [ "addobj", "edit", "delete", "up", "down", "expand" ]; var tbarBtnDisabled = { "addobj": false, "edit": false, "delete": false, + "expand": false, "up": true, - "down": true + "down": true, }; var tbarBtnHidden = { "addobj": true, "edit": true, "delete": true, + "expand": true, "up": true, "down": true }; @@ -295,27 +309,33 @@ Ext.define("OMV.module.admin.storage.zfs.TreePanel", { tbarBtnDisabled["delete"] = true; tbarBtnDisabled["up"] = true; tbarBtnDisabled["down"] = true; + tbarBtnDisabled["expand"] = true; tbarBtnHidden["addobj"] = true; tbarBtnHidden["edit"] = true; tbarBtnHidden["delete"] = true; + tbarBtnHidden["expand"] = true; } else if(records.length == 1) { tbarBtnDisabled["addobj"] = false; tbarBtnDisabled["edit"] = false; tbarBtnDisabled["delete"] = false; tbarBtnDisabled["up"] = false; tbarBtnDisabled["down"] = false; + tbarBtnDisabled["expand"] = false; tbarBtnHidden["addobj"] = false; tbarBtnHidden["edit"] = false; tbarBtnHidden["delete"] = false; + tbarBtnHidden["expand"] = false; } else { tbarBtnDisabled["addobj"] = true; tbarBtnDisabled["edit"] = true; tbarBtnDisabled["delete"] = false; tbarBtnDisabled["up"] = false; tbarBtnDisabled["down"] = false; + tbarBtnDisabled["expand"] = true; tbarBtnHidden["addobj"] = true; tbarBtnHidden["edit"] = true; tbarBtnHidden["delete"] = false; + tbarBtnHidden["expand"] = true; } //Disable 'AddObj' button if selected row is a Snapshot Ext.Array.each(records, function(record) { @@ -334,6 +354,15 @@ Ext.define("OMV.module.admin.storage.zfs.TreePanel", { return false; } }); + + //Disable 'ExpandPool' button if selected row is not a Pool + Ext.Array.each(records, function(record) { + if(!("Pool" == record.get("type"))) { + tbarBtnDisabled["expand"] = true; + tbarBtnHidden["expand"] = true; + return false; + } + }); // Update the button controls. Ext.Array.each(tbarBtnName, function(name) { diff --git a/gui/rpc/zfs.inc b/gui/rpc/zfs.inc index f52f5db..75b5c23 100644 --- a/gui/rpc/zfs.inc +++ b/gui/rpc/zfs.inc @@ -31,6 +31,7 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { $this->registermethod("getSharedParams"); $this->registermethod("createShare"); $this->registermethod("getObjectDetails"); + $this->registermethod("expandPool"); } public function addPool($params, $context) { @@ -525,6 +526,55 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { $output .= implode("\n\r", $out); return array("details" => $output); } + + public function expandPool($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + // Validate the parameters of the RPC service method. + $this->validateMethodParams($params, '{ + "type":"object", + "properties":{ + "pool_type":{"type":"string","enum":["Basic","Mirror",' . + '"Raidz1","Raidz2","Raidz3"]}, + "name":{"type":"string"}, + "devices":{"type":"string"} + } + }'); + $pool = new OMVModuleZFSZpool($params['name']); + switch ($params['pool_type']) { + case "Basic": + $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN; + break; + case "Mirror": + $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR; + break; + case "Raidz1": + $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1; + break; + case "Raidz2": + $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2; + break; + case "Raidz3": + $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3; + break; + default: + throw new OMVModuleZFSException("Incorrect pool type specified"); + break; + } + //Use /dev/disk/by-path as deafult when creating new pools as suggested in ZoL FAQ. + $disks = preg_split("/[,;]/", $params['devices']); + if (file_exists("/dev/disk/by-path/")) { + $tmp_disks = array(); + foreach ($disks as $disk) { + $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk); + } + $disks = $tmp_disks; + } + $vdev[] = new OMVModuleZFSVdev($params['name'], $pooltype, $disks); + $pool->addVdev($vdev); + //Ugly fix to solve the problem of blkid not displaying info on newly created pools + $pool->export(); + $pool->import($pool->getName()); + } } // Register the RPC service. diff --git a/images/zfs_expand.png b/images/zfs_expand.png new file mode 100644 index 0000000000000000000000000000000000000000..4d5881c9eec0fac8716c2da3a9e43e41bc3269fa GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`%ROBjLoEE0Qx5R5Gz9%#c&X3f z7qi*Y9*ZxBO{5*3>N8}nX-hC>h&4XLx2A1@bc46CpYsG>g(Z?{Cm2_hAE-%T7GwDN zv4Tl7l52^X;9Z82z5g$=Y+$ok7s2pS){~9JfjOHwYC)q=pIyisr3r0Iy=E9~unSN) z#KaxnkklQ!!7hcFr?FgetAttribute("allocated"); $tmp['available'] = $pool->getAttribute("free"); $tmp['mountpoint'] = $pool->getMountPoint(); + $vdevs = $pool->getVdevs(); + $vdev_type = $vdevs[0]->getType(); + switch ($vdev_type) { + case OMVModuleZFSVdevType::OMVMODULEZFSMIRROR: + $pool_type = "Mirror"; + break; + case OMVModuleZFSVdevType::OMVMODULEZFSPLAIN: + $pool_type = "Basic"; + break; + case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1: + $pool_type = "Raidz1"; + break; + case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2: + $pool_type = "Raidz2"; + break; + case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3: + $pool_type = "Raidz3"; + break; + } + $tmp['pool_type'] = $pool_type; + $tmp['nr_disks'] = count($vdevs[0]->getDisks()); array_push($objects,$tmp); } else { //This is a Filesystem @@ -255,6 +276,8 @@ class OMVModuleZFSUtil { $available = $ds->getProperty("available"); $tmp['available'] = $available['value']; $tmp['mountpoint'] = $ds->getMountPoint(); + $tmp['pool_type'] = "n/a"; + $tmp['nr_disks'] = "n/a"; array_push($objects,$tmp); } break; @@ -273,6 +296,8 @@ class OMVModuleZFSUtil { $tmp['used'] = "n/a"; $tmp['available'] = "n/a"; $tmp['mountpoint'] = "n/a"; + $tmp['pool_type'] = "n/a"; + $tmp['nr_disks'] = "n/a"; array_push($objects,$tmp); break; @@ -291,6 +316,8 @@ class OMVModuleZFSUtil { $tmp['used'] = "n/a"; $tmp['available'] = "n/a"; $tmp['mountpoint'] = "n/a"; + $tmp['pool_type'] = "n/a"; + $tmp['nr_disks'] = "n/a"; array_push($objects,$tmp); break; -- 2.39.5