require_once("zfs/Snapshot.php");
require_once("zfs/Zvol.php");
require_once("zfs/Zpool.php");
+require_once("zfs/NotifyListener.php");
class OMVRpcServiceZFS extends OMVRpcServiceAbstract {
- public function getName() { return "ZFS";} // RPC Service name. Same as in .js files
+ public function getName() {
+ return "ZFS"; // RPC Service name. Same as in .js files
+ }
+
+ /* Initialize the RPC service. Different methods of the RPC service are declared here*/
+ public function initialize() {
+ $this->registerMethod("addPool");
+ $this->registerMethod("getObjectTree");
+ $this->registermethod("passParam");
+ $this->registermethod("addObject");
+ $this->registermethod("deleteObject");
+ $this->registermethod("getProperties");
+ $this->registermethod("setProperties");
+ $this->registermethod("inherit");
+ $this->registermethod("getSharedParams");
+ $this->registermethod("createShare");
+ $this->registermethod("getObjectDetails");
+ $this->registermethod("expandPool");
+ }
+
+ public function addPool($params, $context) {
+ $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "pooltype":{"type":"string","enum":["basic","mirror",' .
+ '"raidz1","raidz2","raidz3"]},
+ "force":{"type":"boolean"},
+ "mountpoint":{"type":"string"},
+ "name":{"type":"string"},
+ "devices":{"type":"string"},
+ "diskpath":{"type":"boolean"}
+ }
+ }');
+ switch ($params['pooltype']) {
+ 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;
+ }
+ //Check for user supplied options
+ $opts = "";
+ if ($params['force']) {
+ $opts .= "-f ";
+ }
+ if (strlen($params['mountpoint']) > 0) {
+ $opts .= "-m " . $params['mountpoint'] . " ";
+ }
- /* Initialize the RPC service. Different methods of the RPC service are declared here*/
- public function initialize() {
- $this->registerMethod("getObjectTree");
- $this->registermethod("passParam");
- $this->registermethod("addObject");
- $this->registermethod("deleteObject");
- $this->registermethod("getProperties");
- $this->registermethod("setProperties");
- $this->registermethod("inherit");
- $this->registermethod("getSharedParams");
- $this->registermethod("createShare");
+ $disks = preg_split("/[,;]/", $params['devices']);
+ foreach ($disks as $disk) {
+ OMVModuleZFSUtil::setGPTLabel($disk);
+ }
+ //Use /dev/disk/by-path as suggested in ZoL FAQ.
+ if ($params['diskpath']) {
+ try {
+ if (file_exists("/dev/disk/by-path/")) {
+ $tmp_disks = array();
+ foreach ($disks as $disk) {
+ $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk);
+ }
+ $disks = $tmp_disks;
+ }
+ } catch (OMVModuleZFSException $e) {
+ //Do nothing if an excpetion is thrown
+ }
}
+ $vdev = new OMVModuleZFSVdev($params['name'], $pooltype, $disks);
+ $pool = new OMVModuleZFSZpool($vdev, $opts);
+ //Ugly fix to solve the problem of blkid not displaying info on newly created pools
+ $pool->export();
+ $pool->import($pool->getName());
+ }
+
public function getObjectTree($params, $context) {
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
$objects = OMVModuleZFSUtil::getZFSFlatArray();
$new[$a['parentid']][] = $a;
}
$tree = OMVModuleZFSUtil::createTree($new, $new['root']);
+ OMVModuleZFSUtil::addMissingOMVMntEnt(); //Adds missing ZFS filesystems to the OMV core
return $tree;
}
public function passParam($params, $context) {
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
- //$msg = "Key=" . $params['key'] . ";Value=" . $params['value'] . ";";
- //throw new OMVModuleZFSException($msg);
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "key":{"type":"string"},
+ "value":{"type":"string"}
+ }
+ }');
return array($params['key'] => $params['value']);
}
public function addObject($params, $context) {
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "type":{"type":"string","enum":["filesystem","snapshot",' .
+ '"volume","clone"]},
+ "path":{"type":"string"},
+ "name":{"type":"string"},
+ "size":{"type":"string"},
+ "clonename":{"type":"string"},
+ "mountpoint":{"type":"string"}
+ }
+ }');
switch ($params['type']) {
- case "Filesystem":
+ case "clone":
+ $tmp = new OMVModuleZFSSnapshot($params['path']);
+ $tmp->clonesnap($params['clonename']);
+ break;
+ case "filesystem":
$name = $params['path'] . "/" . $params['name'];
$tmp = new OMVModuleZFSDataset($name);
+ if (strlen($params['mountpoint']) > 0) {
+ $properties = array("mountpoint"=>$params['mountpoint']);
+ $tmp->setProperties($properties);
+ }
break;
- case "Snapshot":
+ case "snapshot":
$name = $params['path'] . "@" . $params['name'];
$tmp = new OMVModuleZFSSnapshot($name);
break;
- case "Volume":
+ case "volume":
$name = $params['path'] . "/" . $params['name'];
$tmp = new OMVModuleZFSZvol($name);
$tmp->create($params['size']);
break;
- case "Pool":
- $name = $params['path'] . "/" . $params['name'];
- $tmp = new OMVModuleZFSZpool($name);
- break;
default:
throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
break;
public function deleteObject($params, $context) {
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "type":{"type":"string","enum":["Filesystem","Snapshot",' .
+ '"Volume","Pool"]},
+ "name":{"type":"string"}
+ }
+ }');
+ global $xmlConfig;
+ $name = $params['name'];
switch ($params['type']) {
case "Filesystem":
- case "Clone":
- $name = $params['name'];
+ OMVModuleZFSUtil::deleteShares($name);
$tmp = new OMVModuleZFSDataset($name);
$tmp->destroy();
break;
case "Snapshot":
- $name = $params['name'];
$tmp = new OMVModuleZFSSnapshot($name);
$tmp->destroy();
break;
case "Volume":
- $name = $params['name'];
$tmp = new OMVModuleZFSZvol($name);
$tmp->destroy();
break;
case "Pool":
- $name = $params['name'];
+ $disks = OMVModuleZFSUtil::getDevDisksByPool($name);
+ $pooluuid = OMVModuleZFSUtil::getUUIDbyName($name);
$tmp = new OMVModuleZFSZpool($name);
$tmp->destroy();
+ $xpath = "//system/fstab/mntent[fsname='" . $pooluuid . "' and type='zfs']";
+ $object = $xmlConfig->get($xpath);
+ $xmlConfig->delete($xpath);
+ OMVModuleZFSUtil::clearZFSLabel($disks);
break;
default:
throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
public function getProperties($params, $context) {
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "type":{"type":"string"},
+ "name":{"type":"string"},
+ "start":{"type":"integer"},
+ "limit":{'.$GLOBALS['OMV_JSONSCHEMA_COUNTFIELD'].'},
+ "sortfield":{'.$GLOBALS['OMV_JSONSCHEMA_SORTFIELD'].'},
+ "sortdir":{'.$GLOBALS['OMV_JSONSCHEMA_SORTDIR'].'}
+ }
+ }');
$objects = array();
$name = $params['name'];
switch ($params['type']) {
case "Filesystem":
- case "Clone":
$tmp = new OMVModuleZFSDataset($name);
break;
case "Snapshot":
$tmp = new OMVModuleZFSZvol($name);
break;
case "Pool":
- $tmp = new OMVModuleZFSZpool($name);
- break;
+ $tmp = new OMVModuleZFSZpool($name);
+ break;
default:
throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
break;
public function setProperties($params, $context) {
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
- $objects = array();
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "type":{"type":"string","enum":["Filesystem","Snapshot",' .
+ '"Volume","Pool"]},
+ "name":{"type":"string"},
+ "properties":{"type":"array","items":{
+ "type":"object",
+ "properties":{
+ "property":{"type":"string"},
+ "value":{"type":"string"}}}}
+ }
+ }');
+ global $xmlConfig;
switch ($params['type']) {
case "Filesystem":
- case "Clone":
$tmp = new OMVModuleZFSDataset($params['name']);
break;
case "Snapshot":
break;
}
foreach ($params['properties'] as $property) {
+ unset($objects);
+ $objects = array();
$objects[$property['property']] = $property['value'];
+ $tmp->setProperties($objects);
+ if ((strcmp($property['property'], "mountpoint") === 0) && (strcmp($params['type'], "Filesystem") === 0)) {
+ OMVModuleZFSUtil::relocateFilesystem($params['name']);
+ }
}
- $tmp->setProperties($objects);
}
public function inherit($params, $context) {
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "type":{"type":"string","enum":["Filesystem","Snapshot",' .
+ '"Volume","Pool"]},
+ "name":{"type":"string"},
+ "property":{"type":"string"}
+ }
+ }');
// Create a background process.
$bgStatusFilename = $this->createBgProcStatus();
$pid = $this->fork();
$this->updateBgProcStatus($bgStatusFilename, "outputfilename", $bgOutputFilename);
switch ($params['type']) {
case "Filesystem":
- case "Clone":
$tmp = new OMVModuleZFSDataset($params['name']);
break;
case "Snapshot":
public function getSharedParams($params, $context) {
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "type":{"type":"string"},
+ "name":{"type":"string"}
+ }
+ }');
$objects = array();
- $ds = new OMVModuleZFSDataset($params['name']);
- $mountpoint = $ds->getMountPoint();
+ //$ds = new OMVModuleZFSDataset($params['name']);
+ //$mountpoint = $ds->getMountPoint();
return array(
- "mountpoint" => $mountpoint,
+ //"mountpoint" => $mountpoint,
"name" => $params['name'],
"type" => $params['type']);
}
public function createShare($params, $context) {
global $xmlConfig;
$this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
-
- //Get the UUID of the Pool
- $pooluuid = OMVModuleZFSUtil::getUUIDbyName($params['name']);
- preg_match('/^([A-Za-z0-9]+)\/?.*$/', $params['name'], $result);
- $poolname = $result[1];
- unset($result);
-
- //Check if the UUID is already stored as an mntent object. If it isn't then create it.
- $xpath = "//system/fstab/mntent[fsname=" . $pooluuid . "]";
- $object = $xmlConfig->get($xpath);
- if(is_null($object)) {
- $uuid = OMVUtil::uuid();
- $ds = new OMVModuleZFSDataset($poolname);
- $dir = $ds->getMountPoint();
- $object = array(
- "uuid" => $uuid,
- "fsname" => $pooluuid,
- "dir" => $dir,
- "type" => "zfs",
- "opts" => "rw,relatime,xattr",
- "freq" => "0",
- "passno" => "2"
- );
- $xmlConfig->set("//system/fstab",array("mntent" => $object));
- $dispatcher = &OMVNotifyDispatcher::getInstance();
- $dispatcher->notify(OMV_NOTIFY_CREATE,"org.openmediavault.system.fstab.mntent", $object);
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "name":{"type":"string"},
+ "type":{"type":"string","enum":["Filesystem"]},
+ "sharename":{'.$GLOBALS['OMV_JSONSCHEMA_SHARENAME'].'},
+ "comment":{"type":"string"},
+ "mode":{"type":"string","enum":["700","750","755",'.
+ '"770","775","777"],"optional":true},
+ "mountpoint":{"type":"string"}
+ }
+ }');
+
+ // The field 'reldirpath' may not contain the characters '..'. This
+ // is because of security reasons: the given canonicalized absolute
+ // path MUST be below the given mount point.
+ if(1 == preg_match("/\.\./", $params['mountpoint'])) {
+ throw new OMVException(OMVErrorMsg::E_RPC_SERVICE_METHOD_INVALID_PARAMS,
+ sprintf(gettext("The field '%s' contains forbidden two-dot symbols"), "mountpoint"));
}
-
- //Get the mntent object and fetch it's uuid.
- $object = $xmlConfig->get($xpath);
- $mntentref = $object['uuid'];
-
+
// Prepare the configuration object. Use the name of the shared
// folder as the relative directory name of the share.
switch ($params['type']) {
case "Filesystem":
- case "Clone":
- $tmp = new OMVModuleZFSDataset($name);
+ $tmp = new OMVModuleZFSDataset($params['name']);
break;
default:
throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
break;
}
+ $poolname = OMVModuleZFSUtil::getPoolname($params['name']);
+ $pooluuid = OMVModuleZFSUtil::getUUIDbyName($poolname);
+ $dir = $tmp->getMountPoint();
+
+ //Get the mntent object and fetch it's uuid.
+ $xpath = "//system/fstab/mntent[fsname='" . $pooluuid . "' and dir='" . $dir . "' and type='zfs']";
+ $mountpoint = $xmlConfig->get($xpath);
+ $mntentref = $mountpoint['uuid'];
+
$uuid = OMVUtil::uuid();
- $pathName = $tmp->getMountPoint();
- $subdirs = preg_split('/\//',$pathName);
- $reldirpath = $subdirs[count($subdirs)-1];
+ $pathName = $dir . "/" . trim($params['mountpoint'], "/");
+ $reldirpath = trim($params['mountpoint'], "/");
$object = array(
"uuid" => $uuid,
"name" => $params['sharename'],
- "comment" => $params['comment'],
+ "comment" => $params['comment'] . "*** ZFS share on " . $params['name'] . " ***",
"mntentref" => $mntentref,
"reldirpath" => $reldirpath
);
$object['mode'] = $params['mode'];
}
+ // Create the shared folder directory if necessary.
+ if(FALSE === file_exists($pathName)) {
+ // Create the directory. Note, the function seems to have a bug
+ // when using the mask parameter. E.g. octdec("777") does not
+ // create the correct permissions as expected, thus change the
+ // mode using chmod.
+ if(FALSE === mkdir($pathName, 0700, TRUE)) {
+ throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
+ sprintf("Failed to create the directory '%s'", $pathName));
+ }
+ // Change the directory mode.
+ if(FALSE === chmod($pathName, octdec($object['mode']))) {
+ throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
+ sprintf("Failed to set file mode to '%s' for '%s'",
+ $object['mode'], $pathName));
+ }
+ }
+
// Change group owner of directory to configured default group,
// e.g. 'users'.
if(FALSE === chgrp($pathName, $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'])) {
$dispatcher->notify(OMV_NOTIFY_CREATE,"org.openmediavault.system.shares.sharedfolder", $object);
// Return the configuration object.
return $object;
-
}
+ public function getObjectDetails($params, $context) {
+ $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
+ // Validate the parameters of the RPC service method.
+ $this->validateMethodParams($params, '{
+ "type":"object",
+ "properties":{
+ "name":{"type":"string"},
+ "type":{"type":"string"}
+ }
+ }');
+ $output = "";
+ switch ($params['type']) {
+ case "Filesystem":
+ $output .= "Filesystem details (zfs get all):\n\r\n\r";
+ $cmd = "zfs get all {$params['name']}";
+ break;
+ case "Volume":
+ $output .= "Volume details (zfs get all):\n\r\n\r";
+ $cmd = "zfs get all {$params['name']}";
+ break;
+ case "Snapshot":
+ $output .= "Snapshot details (zfs get all):\n\r\n\r";
+ $cmd = "zfs get all {$params['name']}";
+ break;
+ case "Pool":
+ $output .= "Pool status (zpool status):\n\r\n\r";
+ $cmd = "zpool status {$params['name']}";
+ OMVModuleZFSUtil::exec($cmd,$out,$res);
+ $output .= implode("\n\r", $out);
+ unset($out);
+ $output .= "\n\r\n\rPool details (zpool get all):\n\r\n\r";
+ $cmd = "zpool get all {$params['name']}";
+ OMVModuleZFSUtil::exec($cmd,$out,$res);
+ $output .= implode("\n\r", $out);
+ unset($out);
+ $output .= "\n\r\n\rPool filesystem details (zfs get all):\n\r\n\r";
+ $cmd = "zfs get all {$params['name']}";
+ break;
+ default:
+ throw new OMVModuleZFSException("Incorrect type provided");
+ }
+ OMVModuleZFSUtil::exec($cmd,$out,$res);
+ $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":{
+ "vdevtype":{"type":"string","enum":["basic","mirror",' .
+ '"raidz1","raidz2","raidz3"]},
+ "name":{"type":"string"},
+ "devices":{"type":"string"},
+ "force":{"type":"boolean"},
+ "diskpath":{"type":"boolean"}
+ }
+ }');
+ $pool = new OMVModuleZFSZpool($params['name']);
+ switch ($params['vdevtype']) {
+ 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;
+ }
+ if ($params['force']) {
+ $opts .= "-f ";
+ }
+ $disks = preg_split("/[,;]/", $params['devices']);
+ foreach ($disks as $disk) {
+ OMVModuleZFSUtil::setGPTLabel($disk);
+ }
+ //Use /dev/disk/by-path as suggested in ZoL FAQ.
+ if ($params['diskpath']) {
+ try {
+ if (file_exists("/dev/disk/by-path/")) {
+ $tmp_disks = array();
+ foreach ($disks as $disk) {
+ $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk);
+ }
+ $disks = $tmp_disks;
+ }
+ } catch (OMVModuleZFSException $e) {
+ //Do nothing if an exception is thrown
+ }
+ }
+ $vdev[] = new OMVModuleZFSVdev($params['name'], $pooltype, $disks);
+ $pool->addVdev($vdev, $opts);
+ //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.