From: Niclas Berglind Date: Mon, 13 Oct 2014 14:28:30 +0000 (+0200) Subject: Support for cloned filesystems and volumes. X-Git-Url: http://git.datanom.net/omvzfs.git/commitdiff_plain/bcf5fe520db7b8e44012d46856011752918196f5 Support for cloned filesystems and volumes. Signed-off-by: Niclas Berglind --- diff --git a/gui/js/omv/module/admin/storage/zfs/Overview.js b/gui/js/omv/module/admin/storage/zfs/Overview.js index 4731124..9db8915 100644 --- a/gui/js/omv/module/admin/storage/zfs/Overview.js +++ b/gui/js/omv/module/admin/storage/zfs/Overview.js @@ -221,32 +221,63 @@ Ext.define("OMV.module.admin.storage.zfs.AddObject", { getFormItems: function() { var me = this; + + var store = new Ext.data.ArrayStore({ + autoDestroy: true, + storeId: 'my_store', + fields: [ + {name: 'value', type: 'string'}, + {name: 'display', type: 'string'} + ] + }); + + var combodata; + if (me.parenttype === "Snapshot") { + combodata = [["clone","Clone"]]; + } else if (me.parenttype === "Volume") { + combodata = [["snapshot", "Snapshot"]]; + } else { + combodata = [["filesystem","Filesystem"], + ["volume","Volume"], + ["snapshot","Snapshot"]]; + } + store.loadData(combodata,false); + return [{ xtype: "combo", name: "type", fieldLabel: _("Object Type"), queryMode: "local", - store: [ - [ "filesystem", "Filesystem" ], - [ "snapshot", "Snapshot" ], - [ "volume", "Volume" ] - ], + store: store, allowBlank: true, editable: false, triggerAction: "all", - value: "filesystem", + valueField: "value", + displayField: "display", + value: combodata[0][0], listeners: { scope: me, change: function(combo, value) { var sizeField = this.findField("size"); + var cloneField = this.findField("clonename"); + var nameField = this.findField("name"); switch(value) { case "volume": sizeField.show(); - sizeField.allowBlank = false; + sizeField.allowBlank = false; + cloneField.hide(); + nameField.show(); break; + case "clone": + sizeField.hide(); + sizeField.allowBlank = true; + cloneField.show(); + nameField.hide(); default: sizeField.hide(); - sizeField.allowBlank = true; + sizeField.allowBlank = true; + cloneField.hide(); + nameField.show(); break; } sizeField.validate(); @@ -258,19 +289,68 @@ Ext.define("OMV.module.admin.storage.zfs.AddObject", { fieldLabel: _("Prefix"), allowBlank: false, readOnly: true, - value: me.path + value: me.path, + listeners: { + scope: me, + beforerender: function(e, eOpts) { + var pathField = this.findField("path"); + if (me.parenttype === "Snapshot") { + pathField.fieldLabel = _("Snapshot to clone"); + } else { + pathField.fieldLabel = _("Prefix"); + } + } + } },{ xtype: "textfield", name: "name", + id: "name", fieldLabel: _("Name"), allowBlank: false, plugins: [{ ptype: "fieldinfo", text: _("Name of the new object. Prefix will prepend the name. Please omit leading /") - }] + }], + listeners: { + scope: me, + beforerender: function(e, eOpts) { + var nameField = this.findField("name"); + if (me.parenttype === "Snapshot") { + nameField.hide(); + nameField.allowBlank = true; + } else { + nameField.show(); + nameField.allowBlank = false; + } + } + } + },{ + xtype: "textfield", + name: "clonename", + id: "clonename", + fieldLabel: _("Clone name"), + allowBlank: false, + plugins: [{ + ptype: "fieldinfo", + text: _("Name of the new Clone. It can be placed anywhere within the ZFS hierarchy.") + }], + listeners: { + scope: me, + beforerender: function(e, eOpts) { + var cloneField = this.findField("clonename"); + if (me.parenttype === "Snapshot") { + cloneField.show(); + cloneField.allowBlank = false; + } else { + cloneField.hide(); + cloneField.allowBlank = true; + } + } + } },{ xtype: "textfield", name: "size", + id: "size", hidden: true, fieldLabel: _("Size"), allowBlank: true, @@ -713,7 +793,14 @@ Ext.define("OMV.module.admin.storage.zfs.Overview", { dataIndex: 'name', sortable: true, flex: 2, - stateId: 'name' + stateId: 'name', + renderer: function(value, p, r){ + if (r.data['origin'] === "n/a") { + return r.data['name']; + } else { + return r.data['name'] + ' (' + r.data['origin'] + ')'; + } + } },{ text: _("Type"), dataIndex: 'type', @@ -721,10 +808,10 @@ Ext.define("OMV.module.admin.storage.zfs.Overview", { flex: 1, stateId: 'type', renderer: function(value, p, r){ - if (r.data['type'] == "Pool") { - return r.data['type'] + ' (' + r.data['pool_type'] + ')'; - } else { + if (r.data['origin'] === "n/a") { return r.data['type']; + } else { + return 'Clone'; } } },{ @@ -856,6 +943,7 @@ Ext.define("OMV.module.admin.storage.zfs.Overview", { Ext.create("OMV.module.admin.storage.zfs.AddObject", { title: _("Add Object"), path: record.get("path"), + parenttype: record.get("type"), listeners: { scope: me, submit: function() { diff --git a/gui/js/omv/module/admin/storage/zfs/TreePanel.js b/gui/js/omv/module/admin/storage/zfs/TreePanel.js index 373f5b6..56c3ec0 100644 --- a/gui/js/omv/module/admin/storage/zfs/TreePanel.js +++ b/gui/js/omv/module/admin/storage/zfs/TreePanel.js @@ -337,9 +337,9 @@ Ext.define("OMV.module.admin.storage.zfs.TreePanel", { tbarBtnHidden["delete"] = false; tbarBtnHidden["expand"] = true; } - //Disable 'AddObj' button if selected row is a Snapshot + //Disable 'AddObj' button if selected row is a Clone Ext.Array.each(records, function(record) { - if("Snapshot" == record.get("type")) { + if("Clone" == record.get("type")) { tbarBtnDisabled["addobj"] = true; tbarBtnHidden["addobj"] = true; return false; diff --git a/gui/rpc/zfs.inc b/gui/rpc/zfs.inc index fda53b0..bbab27c 100644 --- a/gui/rpc/zfs.inc +++ b/gui/rpc/zfs.inc @@ -126,13 +126,18 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { "type":"object", "properties":{ "type":{"type":"string","enum":["filesystem","snapshot",' . - '"volume"]}, + '"volume","clone"]}, "path":{"type":"string"}, "name":{"type":"string"}, - "size":{"type":"string"} + "size":{"type":"string"}, + "clonename":{"type":"string"} } }'); switch ($params['type']) { + case "clone": + $tmp = new OMVModuleZFSSnapshot($params['path']); + $tmp->clonesnap($params['clonename']); + break; case "filesystem": $name = $params['path'] . "/" . $params['name']; $tmp = new OMVModuleZFSDataset($name); @@ -159,7 +164,7 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { "type":"object", "properties":{ "type":{"type":"string","enum":["Filesystem","Snapshot",' . - '"Volume","Clone","Pool"]}, + '"Volume","Pool"]}, "name":{"type":"string"} } }'); @@ -171,10 +176,6 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { $tmp = new OMVModuleZFSDataset($name); $tmp->destroy(); break; - case "Clone": - $tmp = new OMVModuleZFSDataset($name); - $tmp->destroy(); - break; case "Snapshot": $tmp = new OMVModuleZFSSnapshot($name); $tmp->destroy(); @@ -217,7 +218,6 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { $name = $params['name']; switch ($params['type']) { case "Filesystem": - case "Clone": $tmp = new OMVModuleZFSDataset($name); break; case "Snapshot": @@ -252,7 +252,7 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { "type":"object", "properties":{ "type":{"type":"string","enum":["Filesystem","Snapshot",' . - '"Volume","Clone","Pool"]}, + '"Volume","Pool"]}, "name":{"type":"string"}, "properties":{"type":"array","items":{ "type":"object", @@ -264,7 +264,6 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { global $xmlConfig; switch ($params['type']) { case "Filesystem": - case "Clone": $tmp = new OMVModuleZFSDataset($params['name']); break; case "Snapshot": @@ -298,7 +297,7 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { "type":"object", "properties":{ "type":{"type":"string","enum":["Filesystem","Snapshot",' . - '"Volume","Clone","Pool"]}, + '"Volume","Pool"]}, "name":{"type":"string"}, "property":{"type":"string"} } @@ -316,7 +315,6 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { $this->updateBgProcStatus($bgStatusFilename, "outputfilename", $bgOutputFilename); switch ($params['type']) { case "Filesystem": - case "Clone": $tmp = new OMVModuleZFSDataset($params['name']); break; case "Snapshot": @@ -368,7 +366,7 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { "type":"object", "properties":{ "name":{"type":"string"}, - "type":{"type":"string","enum":["Filesystem","Clone"]}, + "type":{"type":"string","enum":["Filesystem"]}, "sharename":{'.$GLOBALS['OMV_JSONSCHEMA_SHARENAME'].'}, "comment":{"type":"string"}, "mode":{"type":"string","enum":["700","750","755",'. @@ -391,9 +389,6 @@ class OMVRpcServiceZFS extends OMVRpcServiceAbstract { case "Filesystem": $tmp = new OMVModuleZFSDataset($params['name']); break; - case "Clone": - $tmp = new OMVModuleZFSDataset($params['name']); - break; default: throw new OMVModuleZFSException("Illegal type provided: " . $params['type']); break; diff --git a/src/Utils.php b/src/Utils.php index 58bdc7b..c7049dc 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -226,6 +226,7 @@ class OMVModuleZFSUtil { 'expanded'=>$expanded, 'path'=>$path); $pool = new OMVModuleZFSZpool($path); + $tmp['origin'] = "n/a"; $tmp['size'] = $pool->getSize(); $tmp['used'] = $pool->getAttribute("allocated"); $tmp['available'] = $pool->getAttribute("free"); @@ -243,12 +244,12 @@ class OMVModuleZFSUtil { $ds = new OMVModuleZFSDataset($path); if ($ds->isClone()) { //This is a cloned Filesystem - $tmp['type'] = "Clone"; $tmp['origin'] = $ds->getOrigin(); } else { //This is a standard Filesystem. - $tmp['type']= ucfirst($type); + $tmp['origin'] = "n/a"; } + $tmp['type']= ucfirst($type); $tmp['size'] = "n/a"; $used = $ds->getProperty("used"); $tmp['used'] = $used['value']; @@ -268,7 +269,15 @@ class OMVModuleZFSUtil { 'icon'=>"images/save.png", 'path'=>$path, 'expanded'=>$expanded); - $vol = new OMVModuleZFSZvol(); + $vol = new OMVModuleZFSZvol($path); + if ($vol->isClone()) { + //This is a cloned Volume + $tmp['origin'] = $vol->getOrigin(); + } else { + //This is a standard Volume + $tmp['origin'] = "n/a"; + } + $tmp['type']= ucfirst($type); $tmp['size'] = $vol->getSize(); $tmp['used'] = "n/a"; $tmp['available'] = "n/a"; @@ -287,6 +296,7 @@ class OMVModuleZFSUtil { 'icon'=>'images/zfs_snap.png', 'path'=>$path, 'expanded'=>$expanded); + $tmp['origin'] = "n/a"; $tmp['size'] = "n/a"; $tmp['used'] = "n/a"; $tmp['available'] = "n/a"; diff --git a/src/Zvol.php b/src/Zvol.php index e35af5a..8b65ac0 100644 --- a/src/Zvol.php +++ b/src/Zvol.php @@ -265,6 +265,49 @@ class OMVModuleZFSZvol { $this->snapshots[$snap_name]->destroy(); unset($this->snapshots[$snap_name]); } + + /** + * Check if the Volume is a clone or not. + * + * @return bool + * @access public + */ + public function isClone() { + $origin = $this->getProperty("origin"); + if (strlen($origin["value"]) > 0) { + return true; + } else { + return false; + } + } + + /** + * Get the origin of the Volume if it's a clone. + * + * @return string The name of the origin if it exists. Otherwise an empty string. + * @access public + */ + public function getOrigin() { + if ($this->isClone()) { + $origin = $this->getProperty("origin"); + return $origin['value']; + } else { + return ""; + } + } + + /** + * Promotes the Volume if it's a clone. + * + * @return void + * @access public + */ + public function promote() { + if ($this->isClone()) { + $cmd = "zfs promote " . $this->name . " 2>&1"; + $this->exec($cmd,$out,$res); + } + } /** * Helper function to execute a command and throw an exception on error