<?php
+require_once('openmediavault/object.inc');
+require_once('openmediavault/module.inc');
require_once("Vdev.php");
require_once("Snapshot.php");
require_once("Dataset.php");
require_once("Zvol.php");
+require_once("VdevType.php");
+require_once("Utils.php");
require_once("Exception.php");
/**
* @copyright Michael Rasmussen <mir@datanom.net>
*/
class OMVModuleZFSZpool extends OMVModuleAbstract
- implements OMVNotifyListener {
+ implements OMVINotifyListener {
// Attributes
/**
* Name of pool
*
* @var array $vdevs
* @access private
- * @accociation OMVModuleZFSVdev to vdevs
+ * @association OMVModuleZFSVdev to vdevs
*/
private $vdevs;
*
* @var array $spare
* @access private
- * @accociation OMVModuleZFSVdev to spare
+ * @association OMVModuleZFSVdev to spare
*/
private $spare;
*
* @var array $log
* @access private
- * @accociation OMVModuleZFSVdev to log
+ * @association OMVModuleZFSVdev to log
*/
private $log;
*
* @var array $cache
* @access private
- * @accociation OMVModuleZFSVdev to cache
+ * @association OMVModuleZFSVdev to cache
*/
private $cache;
*
* @var array $snapshot
* @access private
- * @accociation OMVModuleZFSSnapshot to snapshot
+ * @association OMVModuleZFSSnapshot to snapshot
*/
private $snapshot;
*
* @var Dataset $dataset
* @access private
- * @accociation OMVModuleZFSDataset to dataset
+ * @association OMVModuleZFSDataset to dataset
*/
private $dataset;
*
* @var Zvol $zvol
* @access private
- * @accociation OMVModuleZFSZvol to zvol
+ * @association OMVModuleZFSZvol to zvol
*/
private $zvol;
/**
* Constructor
*
- * @param $pool pool this mirror belongs to
+ * @param $vdev OMVModuleZFSVdev or array(OMVModuleZFSVdev)
* @throws OMVModuleZFSException
*/
public function __construct($vdev) {
+ $create_pool = true;
+
if (is_array($vdev)) {
$cmd = $this->getCommandString($vdev);
$name = $vdev[0]->getPool();
$type = $vdev[0]->getType();
- }
- else {
+ } else if ($vdev instanceof OMVModuleZFSVdev) {
$cmd = $this->getCommandString(array($vdev));
$name = $vdev->getPool();
$type = $vdev->getType();
+ } else {
+ // Assume we make an instance of an existing pool
+ $create_pool = false;
}
- $cmd = "zpool create $name $cmd";
- OMVUtil::exec($cmd, $output, $result);
- if ($result)
- throw new OMVModuleZFSException($output);
- else {
- $this->vdevs = array();
- $this->spare = array();
- $this->log = array();
- $this->cache = array();
- $this->features = array();
- $this->name = $name;
- $this->type = $type;
- if (is_array($vdev))
- $this->vdevs = $vdev;
- else
- array_push ($this->vdevs, $vdev);
- $this->size = $this->getAttribute("size");
- $this->mountPoint = $this->getAttribute("mountpoint");
+ $this->vdevs = array();
+ $this->spare = null;
+ $this->log = null;
+ $this->cache = null;
+ $this->features = array();
+ if ($create_pool) {
+ $cmd = "zpool create $name $cmd";
+
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
+ else {
+ $this->name = $name;
+ $this->type = $type;
+ if (is_array($vdev))
+ $this->vdevs = $vdev;
+ else
+ array_push ($this->vdevs, $vdev);
+ $this->size = $this->getAttribute("size");
+ $this->mountPoint = $this->getAttribute("mountpoint");
+ }
+ } else {
+ $this->assemblePool($vdev);
}
}
* @access public
*/
public function addVdev(array $vdevs) {
- $cmd = "zpool add " . $this->getName() . " " . $this->getCommandString($vdevs);
+ $cmd = "zpool add " . $this->name . " " . $this->getCommandString($vdevs);
OMVUtil::exec($cmd, $output, $result);
if ($result)
throw new OMVModuleZFSException($output);
else
$this->vdevs = array_merge($this->vdevs, $vdevs);
+ $this->size = $this->getAttribute("size");
}
/**
if ($cache->getType() != OMVModuleZFSVdevType::OMVMODULEZFSPLAIN)
throw new OMVModuleZFSException("Only a plain Vdev can be added as cache");
- $cmd = "zpool add " . $this->getName() . " cache " . $this->getCommandString($vdevs);
+ $cmd = "zpool add " . $this->name . " cache " . $this->getCommandString($vdevs);
OMVUtil::exec($cmd, $output, $result);
if ($result)
throw new OMVModuleZFSException($output);
* @access public
*/
public function removeCache(array $disks = null) {
- $errors = array();
- $exception = null;
-
if (! $disks)
$disks = $this->cache;
- foreach ($disks as $disk) {
- $cmd = "zpool remove " . $this->getName() . " $disk";
- OMVUtil::exec($cmd, $output, $result);
- if ($result)
- array_push ($errors, $output);
- else
- $this->cache = $this->removeDisk($this->cache, $disk);
- }
+ foreach ($disks as $disk)
+ $dist_str .= "$disk ";
- foreach ($errors as $error) {
- if ($exception)
- $exception .= "\n$error";
- else
- $exception = $error;
+ $cmd = "zpool remove " . $this->name . " $dist_str";
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
+ else {
+ foreach ($disks as $disk)
+ $this->cache = $this->removeDisk($this->cache, $disk);
}
-
- if ($exception)
- throw new OMVModuleZFSException($exception);
}
/**
*
* @param OMVModuleZFSVdev $log
* @return void
+ * @throws OMVModuleZFSException
* @access public
*/
public function addLog(OMVModuleZFSVdev $log) {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ if ($log->getType() == OMVModuleZFSVdevType::OMVMODULEZFSPLAIN ||
+ $log->getType() == OMVModuleZFSVdevType::OMVMODULEZFSMIRROR) {
+ $cmd = "zpool add " . $this->name . " log " . $this->getCommandString($vdevs);
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
+
+ $this->log = $log;
+ } else
+ throw new OMVModuleZFSException("Only a plain Vdev or mirror Vdev can be added as log");
}
/**
* XXX
*
* @return void
+ * @throws OMVModuleZFSException
* @access public
*/
public function removeLog() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ foreach ($this->log as $vdev) {
+ if ($vdev->getType() == OMVModuleZFSVdevType::OMVMODULEZFSMIRROR) {
+ $cmd = "zpool remove " . $this->name . " mirror-$i";
+ } else {
+ $disks = $vdev->getDisks();
+ foreach ($disks as $disk)
+ $dist_str .= "$disk ";
+ $cmd = "zpool remove " . $this->name . " $disk_str";
+ }
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
+ else
+ $this->log = array();
+ }
}
/**
* @access public
*/
public function getLog() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ return $this->log;
}
/**
* XXX
*
- * @param array $spares
+ * @param OMVModuleZFSVdev $spares
* @return void
+ * @throws OMVModuleZFSException
* @access public
*/
- public function addSpare(array $spares) {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ public function addSpare(OMVModuleZFSVdev $spares) {
+ if ($spares->getType() != OMVModuleZFSVdevType::OMVMODULEZFSPLAIN)
+ throw new OMVModuleZFSException("Only a plain Vdev can be added as spares");
+
+ $cmd = "zpool add " . $this->name . " spare " . $this->getCommandString($vdevs);
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
+
+ $disks = $spares->getDisks();
+ foreach ($disks as $disk) {
+ array_push ($this->spare, $disk);
+ }
}
/**
* XXX
*
- * @param Disk $spare
+ * @param array $disks
* @return void
+ * @throws OMVModuleZFSException
* @access public
*/
- public function removeSpare($spare) {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ public function removeSpare(array $disks = null) {
+ if (! $disks)
+ $disks = $this->spare;
+
+ foreach ($disks as $disk)
+ $dist_str .= "$disk ";
+
+ $cmd = "zpool remove " . $this->name . " $dist_str";
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
+ else {
+ foreach ($disks as $disk)
+ $this->spare = $this->removeDisk($this->spare, $disk);
+ }
}
/**
* @access public
*/
public function getSpares() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ return $this->spare;
}
/**
* @access public
*/
public function getSize() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ return $this->size;
}
/**
* @access public
*/
public function getMountPoint() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ return $this->mountPoint;
}
/**
*
* @param array $features
* @return void
+ * @throws OMVModuleZFSException
* @access public
*/
public function setFeatures(array $features) {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ foreach ($features as $feature => $value) {
+ $cmd = "zpool set $feature=$value " . $this->name;
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
+ }
+ $this->features = $this->getAllAttributes();
}
/**
- * XXX
+ * We only return array of features for which the user can
+ * change in GUI.
*
- * @return list<Feature>
+ * @return array of features
* @access public
*/
- public function getFeatures() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ public function getFeatures($internal = true) {
+ $attrs = array();
+ $featureSet = array(
+ 'recordsize', /* default 131072. 512 <= n^2 <= 131072*/
+ 'checksum', /* on | off */
+ 'compression', /* off | lzjb | gzip | zle | lz4 */
+ 'atime', /* on | off */
+ 'aclmode', /* discard | groupmask | passthrough | restricted */
+ 'aclinherit', /* discard | noallow | restricted | passthrough | passthrough-x */
+ 'casesensitivity', /* sensitive | insensitive | mixed */
+ 'primarycache', /* all | none | metadata */
+ 'secondarycache', /* all | none | metadata */
+ 'logbias', /* latency | throughput */
+ 'dedup', /* on | off */
+ 'sync' /* standard | always | disabled */
+ );
+ if (count($this->features) < 1)
+ $this->features = $this->getAllAttributes();
+ if ($internal) {
+ foreach ($this->features as $attr => $val) {
+ if (in_array($attr, $featureSet))
+ $attrs[$attr] = $val['value'];
+ }
+ } else {
+ foreach ($this->features as $attr => $val) {
+ if (in_array($attr, $featureSet))
+ $attrs[$attr] = $val;
+ }
+ }
+
+ return $attrs;
}
/**
* XXX
*
* @return void
+ * @throws OMVModuleZFSException
* @access public
*/
public function export() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ $cmd = "zpool export " . $this->name;
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
}
/**
*
* @param string $name
* @return void
+ * @throws OMVModuleZFSException
* @access public
*/
- public function import($name) {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ public function import($name = null) {
+ if ($name)
+ $cmd = "zpool import $name";
+ else
+ $cmd = "zpool import";
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
}
/**
* XXX
*
* @return void
+ * @throws OMVModuleZFSException
* @access public
*/
public function scrub() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ $cmd = "zpool scrub " . $this->name;
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
}
/**
* XXX
*
* @return string
+ * @throws OMVModuleZFSException
* @access public
*/
public function status() {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ $cmd = "zpool status " . $this->name;
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
}
public function bindListeners(OMVNotifyDispatcher $dispatcher) {
- $dispatcher->addListener(
- OMV_NOTIFY_EVENT,
- "org.openmediavault.module.service.nfs.start",
- array($this, "onNotify"));
- $dispatcher->addListener(
- OMV_NOTIFY_EVENT,
- "org.openmediavault.module.service.nfs.stop",
- array($this, "onNotify"));
- $dispatcher->addListener(
- OMV_NOTIFY_EVENT,
- "org.openmediavault.module.service.nfs.applyconfig",
- array($this, "onNotify"));
+ // Update service if configuration has been modified
+ $dispatcher->addListener(
+ OMV_NOTIFY_MODIFY,
+ "org.openmediavault.services.nfs",
+ array($this, "onUpdateNFSService"));
+ $dispatcher->addListener(
+ OMV_NOTIFY_CREATE,
+ "org.openmediavault.services.nfs.shares.share",
+ array($this, "onCreateNFSShare"));
+ $dispatcher->addListener(
+ OMV_NOTIFY_DELETE,
+ "org.openmediavault.services.nfs.shares.share",
+ array($this, "onDeleteNFSShare"));
+ $dispatcher->addListener(
+ OMV_NOTIFY_MODIFY,
+ "org.openmediavault.services.nfs.shares.share",
+ array($this, "onUpdateNFSShare"));
}
/**
* XXX
- * org.openmediavault.module.service.<servicename>.start
- * org.openmediavault.module.service.<servicename>.stop
- * org.openmediavault.module.service.<servicename>.applyconfig
+ * org.openmediavault.services.nfs
*
* @param string event
* @access public
*/
- public function onNotify($event) {
- trigger_error('Not Implemented!', E_USER_WARNING);
+ public function onUpdateNFSService($args) {
+ $this->debug(sprintf("onUpdateNFSService args=%s", var_export($args, true)));
+ }
+
+ /**
+ * XXX
+ * org.openmediavault.services.nfs.shares.share
+ *
+ * @param string event
+ * @access public
+ */
+ public function onCreateNFSShare($args) {
+ $this->debug(sprintf("onCreateNFSShare args=%s", var_export($args, true)));
+ }
+
+ /**
+ * XXX
+ * org.openmediavault.services.nfs.shares.share
+ *
+ * @param string event
+ * @access public
+ */
+ public function onDeleteNFSShare($args) {
+ $this->debug(sprintf("onDeleteNFSShare args=%s", var_export($args, true)));
+ }
+
+ /**
+ * XXX
+ * org.openmediavault.services.nfs.shares.share
+ *
+ * @param string event
+ * @access public
+ */
+ public function onUpdateNFSShare($args) {
+ $this->debug(sprintf("onUpdateNFSShare args=%s", var_export($args, true)));
+ }
+
+ /**
+ * Get a single property value associated with the Dataset
+ *
+ * @param string $property Name of the property to fetch
+ * @return array The returned array with the property. The property is an associative array with
+ * two elements, <value> and <source>.
+ * @access public
+ */
+ public function getProperty($property) {
+ $attrs = $this->getFeatures(false);
+ return $attrs["$property"];
+ }
+
+ /**
+ * Get an associative array of all properties associated with the Snapshot
+ *
+ * @return array $properties Each entry is an associative array with two elements
+ * <value> and <source>
+ * @access public
+ */
+ public function getProperties() {
+ $attrs = $this->getFeatures(false);
+ return $attrs;
+ }
+
+ /**
+ * Sets a number of Dataset properties. If a property is already set it will be updated with the new value.
+ *
+ * @param array $properties An associative array with properties to set
+ * @return void
+ * @access public
+ */
+ public function setProperties($properties) {
+ foreach ($properties as $newpropertyk => $newpropertyv) {
+ $cmd = "zfs set " . $newpropertyk . "=" . $newpropertyv . " " . $this->name . " 2>&1";
+ OMVModuleZFSUtil::exec($cmd,$out,$res);
+ $attr = $this->getAttribute($newpropertyk);
+ $this->features[$newpropertyk] = $attr;
+ }
+ }
+
+ /**
+ * Destroy the Dataset.
+ *
+ * @return void
+ * @access public
+ */
+ public function destroy() {
+ $cmd = "zpool destroy " . $this->name . " 2>&1";
+ $this->exec($cmd,$out,$res);
+ }
+
+ /**
+ * Clears a previously set proporty and specifies that it should be
+ * inherited from it's parent.
+ *
+ * @param string $property Name of the property to inherit.
+ * @return void
+ * @access public
+ */
+ public function inherit($property) {
+ $cmd = "zfs inherit " . $property . " " . $this->name . " 2>&1";
+ $this->exec($cmd,$out,$res);
+ $attr = $this->getAttribute($newpropertyk);
+ $this->features[$newpropertyk] = $attr;
}
/**
$adds = array();
foreach ($vdevs as $vdev) {
+ if (is_object($vdev) == false)
+ throw new OMVMODULEZFSException("Not object of class OMVModuleZFSVdev");
+ if (is_a($vdev, OMVModuleZFSVdev) == false)
+ throw new OMVMODULEZFSException("Object is not of class OMVModuleZFSVdev");
$type = $vdev->getType();
$command = "";
array_push ($adds, $command . $diskStr);
}
- return join(" ", $adds);
+ return implode(" ", $adds);
}
/**
return $output;
}
+ /**
+ * Get all attributes from pool
+ * @return array of attributes
+ * @throws OMVModuleZFSException
+ */
+ private function getAllAttributes() {
+ $attrs = array();
+ $cmd = "zfs get -H all {$this->name}";
+
+ try {
+ OMVUtil::exec($cmd, $output, $result);
+ } catch (OMVModuleZFSException $e) {}
+ if ($result)
+ throw new OMVModuleZFSException($output);
+ $output = implode("\n", $output);
+ $res = preg_match_all("/{$this->name}\s+(\w+)\s+([\w\d\.]+)\s+(\w+).*/", $output, $matches, PREG_SET_ORDER);
+ if ($res == false || $res == 0)
+ throw new OMVModuleZFSException("Error return by zpool get all: $output");
+ foreach ($matches as $match) {
+ $attrs[$match[1]] = array('value' => $match[2], 'source' => $match[3]);
+ }
+
+ return $attrs;
+ }
+
+ /**
+ * Get all Dataset properties from commandline and update object properties attribute
+ *
+ * @return void
+ * @access private
+ */
+ private function updateAllProperties() {
+ $this->features = $this->getAllAttributes();
+ }
+
/**
* Remove a disk from array
*
return $new_disks;
}
+
+ /**
+ * Construct existing pool
+ *
+ * @param string $name
+ * @return void
+ * @throws OMVModuleZFSException
+ */
+ private function assemblePool($name) {
+ $cmd = "zpool status -v $name";
+ $types = 'mirror|raidz1|raidz2|raidz3';
+ $dev = null;
+ $type = null;
+ $log = false;
+ $cache = false;
+ $start = true;
+
+ OMVUtil::exec($cmd, $output, $result);
+ if ($result)
+ throw new OMVModuleZFSException($output);
+
+ $this->name = $name;
+ foreach($output as $line) {
+ if (! strstr($line, PHP_EOL))
+ $line .= PHP_EOL;
+ if ($start) {
+ if (preg_match("/^\s*NAME/", $line))
+ $start = false;
+ continue;
+ } else {
+ if (preg_match("/^\s*$/", $line)) {
+ if ($dev) {
+ $this->output($part, $type, $dev);
+ }
+ break;
+ } else if (preg_match("/^\s*($name|logs|cache|spares)/", $line, $match)) {
+ if ($dev) {
+ $this->output($part, $type, $dev);
+ $dev = null;
+ $type = null;
+ }
+ $part = $match[1];
+ } else {
+ switch ($part) {
+ case $name:
+ if (preg_match("/^\s*($types)/", $line, $match)) {
+ /* new vdev */
+ if ($type) {
+ $this->output(null, $type, $dev);
+ $dev = null;
+ }
+ $type = $match[1];
+ } else if (preg_match("/^\s*([\w\d-]+)\s+/", $line, $match)) {
+ if ($dev)
+ $dev .= " $match[1]";
+ else
+ $dev = "$match[1]";
+ }
+ break;
+ case 'logs':
+ if (preg_match("/^\s*([\w\d-]+)\s+/", $line, $match)) {
+ if ($dev)
+ $dev .= " $match[1]";
+ else
+ $dev = "$match[1]";
+ }
+ break;
+ case 'cache':
+ case 'spares':
+ if (preg_match("/^\s*([\w\d-]+)\s+/", $line, $match)) {
+ if ($dev)
+ $dev .= " $match[1]";
+ else
+ $dev = "$match[1]";
+ }
+ break;
+ default:
+ throw new Exception("$part: Unknown pool part");
+ }
+ }
+ }
+ }
+ $this->size = $this->getAttribute("size");
+ $this->mountPoint = $this->getAttribute("mountpoint");
+ }
+
+ /**
+ * Create pool config from parsed input
+ *
+ * @param string $part
+ * @param string $type
+ * @param string $dev
+ * @return void
+ * @throws OMVModuleZFSException
+ */
+ private function output($part, $type, $dev) {
+ $disks = split(" ", $dev);
+ switch ($part) {
+ case 'logs':
+ if ($type && $type != 'mirror')
+ throw new Exception("$type: Logs can only be mirror or plain");
+ if ($type)
+ $this->log = new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSMIRROR, $disks);
+ else
+ $this->log = new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSPLAIN, $disks);
+ break;
+ case 'cache':
+ if ($type)
+ throw new Exception("$type: cache can only be plain");
+ $this->cache = new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSPLAIN, $disks);
+ break;
+ case 'spares':
+ if ($type)
+ throw new Exception("$type: spares can only be plain");
+ $this->spare = new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSPLAIN, $disks);
+ break;
+ default:
+ if ($type) {
+ switch ($type) {
+ case 'mirror':
+ array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSMIRROR, $disks));
+ $this->type = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR;
+ break;
+ case 'raidz1':
+ array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1, $disks));
+ $this->type = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1;
+ break;
+ case 'raidz2':
+ array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2, $disks));
+ $this->type = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2;
+ break;
+ case 'raidz3':
+ array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3, $disks));
+ $this->type = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3;
+ break;
+ }
+ } else {
+ array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSPLAIN, $disks));
+ $this->type = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN;
+ }
+ break;
+ }
+ }
+
}
?>