X-Git-Url: http://git.datanom.net/omvzfs.git/blobdiff_plain/63617ac219787335b77eb40d276b41546420c763..47e63d33372ed4f7924466ba066fc2a42e1b7c41:/src/Zpool.php diff --git a/src/Zpool.php b/src/Zpool.php index ef4f835..56ec8c6 100644 --- a/src/Zpool.php +++ b/src/Zpool.php @@ -1,377 +1,849 @@ */ -class OMVModuleZFSZpool { +class OMVModuleZFSZpool extends OMVModuleAbstract { // Attributes /** - * XXX + * Name of pool * * @var string $name * @access private */ - private $_name; + private $name; /** - * XXX + * List of Vdev * - * @var list $vdevs + * @var array $vdevs * @access private + * @association OMVModuleZFSVdev to vdevs */ - private $_vdevs; + private $vdevs; /** - * XXX + * List of spares * - * @var list $spare + * @var array $spare * @access private + * @association OMVModuleZFSVdev to spare */ - private $_spare; + private $spare; /** - * XXX + * List of log * - * @var Log $log + * @var array $log * @access private + * @association OMVModuleZFSVdev to log */ - private $_log; + private $log; /** - * XXX + * List of cache * - * @var Cache $cache + * @var array $cache * @access private + * @association OMVModuleZFSVdev to cache */ - private $_cache; + private $cache; /** - * XXX + * Pool size * * @var int $size * @access private */ - private $_size; + private $size; /** - * XXX + * Pool's mountpoint * * @var string $mountPoint * @access private */ - private $_mountPoint; + private $mountPoint; /** - * XXX + * List of features * - * @var list $features + * @var array $features * @access private */ - private $_features; + private $features; // Associations /** - * XXX + * Array of OMVModuleZFSSnapshot. * - * @var Snapshot $unnamed + * @var array $snapshot * @access private - * @accociation Snapshot to unnamed + * @association OMVModuleZFSSnapshot to snapshot */ - #var $unnamed; + private $snapshot; /** - * XXX + * Array of OMVModuleZFSDataset * - * @var Dataset $unnamed + * @var Dataset $dataset * @access private - * @accociation Dataset to unnamed + * @association OMVModuleZFSDataset to dataset */ - #var $unnamed; + private $dataset; /** - * XXX + * Array of OMVModuleZFSZvol * - * @var Zvol $unnamed + * @var Zvol $zvol * @access private - * @accociation Zvol to unnamed + * @association OMVModuleZFSZvol to zvol */ - #var $unnamed; - - /** - * XXX - * - * @var Vdev $unnamed - * @access private - * @accociation Vdev to unnamed - */ - #var $unnamed; + private $zvol; // Operations + /** + * Constructor + * + * @param $vdev OMVModuleZFSVdev or array(OMVModuleZFSVdev) + * @throws OMVModuleZFSException + */ + + public function __construct($vdev, $opts = "") { + $create_pool = true; + + if (is_array($vdev)) { + $cmd = $this->getCommandString($vdev); + $name = $vdev[0]->getPool(); + $type = $vdev[0]->getType(); + } 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; + } + + $this->vdevs = array(); + $this->spare = null; + $this->log = null; + $this->cache = null; + $this->features = array(); + if ($create_pool) { + $cmd = "zpool create $opts$name $cmd 2>&1"; + + OMVUtil::exec($cmd, $output, $result); + if ($result) + throw new OMVModuleZFSException(implode("\n", $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); + } + } + /** - * XXX + * Get pool name * - * @return string XXX + * @return string * @access public */ public function getName() { - trigger_error('Not Implemented!', E_USER_WARNING); + return $this->name; } /** - * XXX + * Get array of Vdev * - * @return list XXX + * @return array * @access public */ public function getVdevs() { - trigger_error('Not Implemented!', E_USER_WARNING); + return $this->vdevs; } /** - * XXX + * Add Vdev to pool * - * @param Vdev $vdev XXX - * @return void XXX + * @param array $vdev array of OMVModuleZFSVdev + * @return void + * @throws OMVModuleZFSException * @access public */ - public function addVdev($vdev) { - trigger_error('Not Implemented!', E_USER_WARNING); + public function addVdev(array $vdevs, $opts= "") { + $cmd = "zpool add " . $this->name . " " . $opts . $this->getCommandString($vdevs) . " 2>&1"; + OMVUtil::exec($cmd, $output, $result); + if ($result) + throw new OMVModuleZFSException(implode("\n", $output)); + else + $this->vdevs = array_merge($this->vdevs, $vdevs); + $this->size = $this->getAttribute("size"); } /** * XXX * - * @param Vdev $vdev XXX - * @return void XXX + * @param OMVModuleZFSVdev $vdev + * @return void + * @throws OMVModuleZFSException * @access public */ - public function removeVdev($vdev) { - trigger_error('Not Implemented!', E_USER_WARNING); + public function removeVdev(OMVModuleZFSVdev $vdev) { + throw new OMVModuleZFSException("Cannot remove vdevs from a pool"); } /** * XXX * - * @param Cache $cache XXX - * @return void XXX + * @param OMVModuleZFSVdev $cache + * @return void + * @throws OMVModuleZFSException * @access public */ - public function addCache($cache) { - trigger_error('Not Implemented!', E_USER_WARNING); + public function addCache(OMVModuleZFSVdev $cache) { + if ($cache->getType() != OMVModuleZFSVdevType::OMVMODULEZFSPLAIN) + throw new OMVModuleZFSException("Only a plain Vdev can be added as cache"); + + $cmd = "zpool add " . $this->name . " cache " . $this->getCommandString($vdevs); + OMVUtil::exec($cmd, $output, $result); + if ($result) + throw new OMVModuleZFSException($output); + + $disks = $cache->getDisks(); + foreach ($disks as $disk) { + array_push ($this->cache, $disk); + } } /** * XXX * - * @return void XXX + * @param array $disks + * @return void + * @throws OMVModuleZFSException * @access public */ - public function removeCache() { - trigger_error('Not Implemented!', E_USER_WARNING); + public function removeCache(array $disks = null) { + if (! $disks) + $disks = $this->cache; + + 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->cache = $this->removeDisk($this->cache, $disk); + } } /** * XXX * - * @return Cache XXX + * @return Cache * @access public */ public function getCache() { - trigger_error('Not Implemented!', E_USER_WARNING); + return $this->cache; } /** * XXX * - * @param Log $log XXX - * @return void XXX + * @param OMVModuleZFSVdev $log + * @return void + * @throws OMVModuleZFSException * @access public */ - public function addLog($log) { - trigger_error('Not Implemented!', E_USER_WARNING); + public function addLog(OMVModuleZFSVdev $log) { + 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 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(); + } } /** * XXX * - * @return Log XXX + * @return Log * @access public */ public function getLog() { - trigger_error('Not Implemented!', E_USER_WARNING); + return $this->log; } /** * XXX * - * @param Disk $spare XXX - * @return void XXX + * @param OMVModuleZFSVdev $spares + * @return void + * @throws OMVModuleZFSException * @access public */ - public function addSpare($spare) { - 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 XXX - * @return void XXX + * @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); + } } /** * XXX * - * @return list XXX + * @return list * @access public */ public function getSpares() { - trigger_error('Not Implemented!', E_USER_WARNING); + return $this->spare; } /** * XXX * - * @return int XXX + * @return int * @access public */ public function getSize() { - trigger_error('Not Implemented!', E_USER_WARNING); + return $this->size; } /** * XXX * - * @return string XXX + * @return string * @access public */ public function getMountPoint() { - trigger_error('Not Implemented!', E_USER_WARNING); + return $this->mountPoint; } /** * XXX * - * @param list $features XXX - * @return void XXX + * @param array $features + * @return void + * @throws OMVModuleZFSException * @access public */ - public function setFeatures($features) { - trigger_error('Not Implemented!', E_USER_WARNING); + public function setFeatures(array $features) { + 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 XXX + * @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 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); } /** * XXX * - * @param string $name XXX - * @return void XXX + * @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 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 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); } /** - * XXX - * org.openmediavault.module.service..start - * org.openmediavault.module.service..stop - * org.openmediavault.module.service..applyconfig + * Get a single property value associated with the Dataset * - * @param string event + * @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, and . * @access public */ - public function onNotify($event) { - trigger_error('Not Implemented!', E_USER_WARNING); + public function getProperty($property) { + $attrs = $this->getFeatures(false); + return $attrs["$property"]; } /** - * XXX + * Get an associative array of all properties associated with the Snapshot * + * @return array $properties Each entry is an associative array with two elements + * and * @access public */ - public function applyConfig() { - trigger_error('Not Implemented!', E_USER_WARNING); + public function getProperties() { + $attrs = $this->getFeatures(false); + return $attrs; } /** - * XXX + * 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 stopService() { - trigger_error('Not Implemented!', E_USER_WARNING); + 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; + } } /** - * XXX + * Destroy the Dataset. * + * @return void * @access public */ - public function startService() { - trigger_error('Not Implemented!', E_USER_WARNING); + 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; } -} + /** + * Convert array of Vdev to command string + * + * @param array $vdevs + * @return string + * @throws OMVMODULEZFSException + */ + private function getCommandString(array $vdevs) { + $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 = ""; + + switch ($type) { + case OMVModuleZFSVdevType::OMVMODULEZFSPLAIN: break; + case OMVModuleZFSVdevType::OMVMODULEZFSMIRROR: $command = "mirror"; break; + case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1: $command = "raidz1"; break; + case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2: $command = "raidz2"; break; + case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3: $command = "raidz3"; break; + default: + throw new OMVMODULEZFSException("Unknown Vdev type"); + } + $disks = $vdev->getDisks(); + $diskStr = ""; + foreach($disks as $disk) { + $diskStr .= " $disk"; + } + + array_push ($adds, $command . $diskStr); + } + + return implode(" ", $adds); + } + + /** + * Get an attribute from pool + * + * @param string $attribute + * @return string value + */ + public function getAttribute($attribute) { + $cmd = "zpool list -H -o $attribute {$this->name}"; + OMVUtil::exec($cmd, $output, $result); + if ($result) { + $cmd = "zfs list -H -o $attribute {$this->name}"; + OMVUtil::exec($cmd, $output, $result); + if ($result) + return null; + } + + 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 + * + * @param array $array + * @param string $disk + * @return array + */ + private function removeDisk(array $array, $disk) { + $new_disks = array(); + + foreach ($array as $item) { + if (strcmp($item, $disk) != 0) + array_push ($new_disks, $item); + } + + 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-a-z0-9\:\.\-]+)\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; + } + } + +} ?>