*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/**
* @class OMVModuleZPool.
*/
require ('OMVModulePoolType.php');
require ('OMVModulePoolAction.php');
class OMVModuleZPool {
private $params;
private $log;
private $mirror_log;
private $cache;
private $spare;
private $alloc;
private $status; // one of null, ok, degraded, faulted, scrub, resilver
private $debug;
/**
* Constructor of class OMVModuleZPool.
* @param params Array which can contain all or a subset of
* the following fields:
*
* - atime Either true or false. Default is true.
*
- compress Either true or false. Default is false.
*
- dedub Either true or false. Default is false.
*
- name Name for pool
*
- size Size of pool in either MB or GB. 100M or 100G.
*
- sync Either true or false. Default is false.
*
- type See OMVModulePoolType.
*
- vdevs Number of vdevs in pool. Default is 1.
*
* @param options Array which can contain all or a subset of
* the following fields:
*
* - log Array which contains the following fields:
*
* - disks Array of disks. Format: /dev/disk/by-id/.....
*
- mirror Either true or false. Default is false.
*
* - cache Array which contains the following fields:
*
* - disks Array of disks. Format: /dev/disk/by-id/.... Default use internal.
*
* - spare Array of disks. Format: /dev/disk/by-id/...
*
* @param debug Print debug information to STDERR.
* @throws Exception.
*/
public function __construct(array $params = array(), array $options = array(), $debug = false) {
$this->params = array(
'atime' => 'on',
'compress' => 'off',
'dedub' => 'off',
'name' => '',
'size' => '0M',
'sync' => 'off',
'type' => OMVModulePoolType::OMVModulePoolType_TYPE_NONE,
'vdevs' => 1
);
$this->debug = $debug;
foreach ($params as $key => $value) {
if (array_key_exists($key, $this->params)) {
$this->validate($key, $value);
$this->params[$key] = $value;
} else {
throw new Exception("$key: Not valid parameter");
}
}
foreach ($options as $key => $value) {
$this->validate($key, $value, true);
}
$this->status = null;
if ($this->debug) {
fprintf(STDERR, "OMVModuleZPool instantiate with the following params\n");
foreach ($this->params as $key => $value) {
$param = ($key == 'type') ? OMVModulePoolType::toString($value) : $value;
fprintf(STDERR, " %-8s => %s\n", $key, $param);
}
}
}
/**
* Create pool.
* @param disks Array of disks. Format: /dev/disk/by-id/...
* @param name Name for the pool.
* @param options Array which can contain all or a subset of
* the following fields:
*
* - log Array which contains the following fields:
*
* - disks Array of disks. Format: /dev/disk/by-id/...
*
- mirror Either true or false. Default is false.
*
* - cache Array which contains the following fields:
*
* - disks Array of disks. Format: /dev/disk/by-id/... Default use internal.
*
* - spare Array of disks. Format: /dev/disk/by-id/...
*
* @throws Exception.
*/
public function createPool(array $disks, $name = null, array $options = array()) {
return $this->updatePool(OMVModulePoolAction::OMVModulePoolAction_TYPE_CREATE,
$disks, count($disks), $name, $options);
}
/**
* Add pool.
* @param disks Array of disks. Format: /dev/disk/by-id/...
* @param name Name for the pool.
* @param options Array which can contain all or a subset of
* the following fields:
*
* - log Array which contains the following fields:
*
* - disks Array of disks. Format: /dev/disk/by-id/...
*
- mirror Either true or false. Default is false.
*
* - cache Array which contains the following fields:
*
* - disks Array of disks. Format: /dev/disk/by-id/... Default use internal.
*
* - spare Array of disks. Format: /dev/disk/by-id/...
*
* @throws Exception.
*/
public function addPool(array $disks, $name = null, array $options = array()) {
$this->initPool($name);
return $this->updatePool(OMVModulePoolAction::OMVModulePoolAction_TYPE_ADD,
$disks, count($disks), $name, $options);
}
public function removeDevice(array $disks, $name = null) {
}
public function destroyPool($name = null) {
}
public function scrubPool($name = null) {
}
public function exportPool($name = null){
}
public function importPool($name = null){
}
/*
* public function attachDevice($extingDisk, $newDisk, $name = null)
* public function detachDevice($disk, $name = null)
* public function onlineDevice($disk, $name = null)
* public function offlineDevice($disk, $name = null)
* public function replaceDevice($extingDisk, $newDisk, $name = null)
* public function upgradePool($name = null)
*/
private function initPool($name = null) {
// Replace with OMVUtil::exec
$this->validateName($name);
}
private function validateName($name = null) {
$oldname = null;
if ($name != null) {
$oldname = $this->params['name'];
$this->params['name'] = $name;
}
if (! $this->params['name'] || ! preg_match('/^\w+$/', $this->params['name'])) {
if ($oldname) {
$this->params['name'] = $oldname;
}
throw new Exception("Name of pool cannot be null or empty");
}
}
/**
* Update pool.
* @param action Action to be taken. create or add.
* @param disks Array of disks. Format: /dev/disk/by-id/...
* @param disk_num Number of disk(s) in the pool.
* @param name Name for the pool.
* @param options Array which can contain all or a subset of
* the following fields:
*
* - log Array which contains the following fields:
*
* - disks Array of disks. Format: /dev/disk/by-id/...
*
- mirror Either true or false. Default is false.
*
* - cache Array which contains the following fields:
*
* - disks Array of disks. Format: /dev/disk/by-id/... Default use internal.
*
* - spare Array of disks. Format: /dev/disk/by-id/...
*
* @throws Exception.
*/
private function updatePool($action, array $disks, $disk_num, $name = null, array $options = array()) {
OMVModulePoolAction::toString($action);
$cmd = 'zpool ' . OMVModulePoolAction::getAction($action);
if ($disk_num < 1) {
throw new Exception("Pool must have at least 1 disk");
};
$this->validateName($name);
foreach ($options as $key => $value) {
$this->validate($key, $value, true);
}
switch ($action) {
case OMVModulePoolAction::OMVModulePoolAction_TYPE_CREATE:
$cmd .= ' ' . $this->params['name'];
break;
case OMVModulePoolAction::OMVModulePoolAction_TYPE_ADD:
$cmd .= ' ' . $this->params['name'];
break;
}
switch ($this->params['type']) {
case OMVModulePoolType::OMVModulePoolType_TYPE_NONE:
print "Create basic pool\n";
if ($this->params['vdevs'] != 1) {
throw new Exception("A basic zpool can only have 1 vdev");
}
$vdev = '';
foreach ($disks as $disk) {
$vdev .= ($vdev) ? " $disk" : "$disk";
}
$cmd .= " $vdev";
break;
case OMVModulePoolType::OMVModulePoolType_TYPE_MIRROR:
print "Create mirrored pool\n";
if ($disk_num / $this->params['vdevs'] < 2 ||
($disk_num / $this->params['vdevs']) % $this->params['vdevs']) {
throw new Exception("$disk_num disk(s) cannot be evenly distributed to ".
$this->params['vdevs']." vdev(s) and form a proper stripe or mirror");
}
$vdevs = array();
$disk_sum = $disk_num / $this->params['vdevs'];
$s = '';
for ($i = 1, $num = 0; $i <= $disk_num; $i++) {
$s .= ($s) ? " ${disks[$i-1]}" : "${disks[$i-1]}";
if ($i % $disk_sum == 0) {
$vdevs[$num++] = $s;
$s = '';
}
}
foreach ($vdevs as $vdev) {
$cmd .= " mirror $vdev";
}
break;
case OMVModulePoolType::OMVModulePoolType_TYPE_RAIDZ1:
print "Create raidz1 pool\n";
if ($disk_num / $this->params['vdevs'] < 3 ||
($disk_num / $this->params['vdevs']) % $this->params['vdevs']) {
throw new Exception("$disk_num disk and ".$this->params['vdevs'].
" vdev(s) cannot provide mininum 3 disks per vdev for raidz1");
}
$vdevs = array();
$disk_sum = $disk_num / $this->params['vdevs'];
$s = '';
for ($i = 1, $num = 0; $i <= $disk_num; $i++) {
$s .= ($s) ? " ${disks[$i-1]}" : "${disks[$i-1]}";
if ($i % $disk_sum == 0) {
$vdevs[$num++] = $s;
$s = '';
}
}
foreach ($vdevs as $vdev) {
$cmd .= " raidz1 $vdev";
}
break;
case OMVModulePoolType::OMVModulePoolType_TYPE_RAIDZ2:
print "Create raidz2 pool\n";
if ($disk_num / $this->params['vdevs'] < 4 ||
($disk_num / $this->params['vdevs']) % $this->params['vdevs']) {
throw new Exception("$disk_num disk and ".$this->params['vdevs'].
" vdev(s) cannot provide mininum 4 disks per vdev for raidz2");
}
$vdevs = array();
$disk_sum = $disk_num / $this->params['vdevs'];
$s = '';
for ($i = 1, $num = 0; $i <= $disk_num; $i++) {
$s .= ($s) ? " ${disks[$i-1]}" : "${disks[$i-1]}";
if ($i % $disk_sum == 0) {
$vdevs[$num++] = $s;
$s = '';
}
}
foreach ($vdevs as $vdev) {
$cmd .= " raidz2 $vdev";
}
break;
case OMVModulePoolType::OMVModulePoolType_TYPE_RAIDZ3:
print "Create raidz3 pool\n";
if ($disk_num / $this->params['vdevs'] < 5 ||
($disk_num / $this->params['vdevs']) % $this->params['vdevs']) {
throw new Exception("$disk_num disk and ".$this->params['vdevs'].
" vdev(s) cannot provide mininum 5 disks per vdev for raidz3");
}
$vdevs = array();
$disk_sum = $disk_num / $this->params['vdevs'];
$s = '';
for ($i = 1, $num = 0; $i <= $disk_num; $i++) {
$s .= ($s) ? " ${disks[$i-1]}" : "${disks[$i-1]}";
if ($i % $disk_sum == 0) {
$vdevs[$num++] = $s;
$s = '';
}
}
foreach ($vdevs as $vdev) {
$cmd .= " raidz3 $vdev";
}
break;
}
foreach ($options as $key => $value) {
switch ($key) {
case 'log':
$cmd .= " log";
if ($value['mirror'] === true) {
$cmd .= " mirror";
}
foreach ($value['disks'] as $disk) {
$cmd .= " $disk";
}
break;
case 'cache':
$cmd .= " cache";
foreach ($value as $disk) {
$cmd .= " $disk";
}
break;
case 'spare':
$cmd .= " spare";
foreach ($value as $disk) {
$cmd .= " $disk";
}
break;
}
}
if ($this->debug) {
fprintf(STDERR, "%s\n", $cmd);
}
return $cmd;
}
/**
* Validate options.
* @param key Option to validate.
* @param value Value for option to validate.
* @param type See OMVModulePoolType.
* @throws Exception if option is invalid.
*/
private function validate($key, $value, $options = false) {
if ($options) {
switch ($key) {
case 'log':
if (! is_array($value) || ! array_key_exists('mirror', $value) ||
! array_key_exists('disks', $value)) {
throw new Exception("log option missing disks array or mirror value");
}
if ($value['mirror'] === true && count($value['disks']) != 2) {
throw new Exception("Two disks is required to form a log mirror");
}
$this->log = $value['disks'];
$this->mirror_log = $value['mirror'];
break;
case 'cache':
if (! is_array($value)) {
throw new Exception("log option missing disks array or mirror value");
}
$this->cache = $value;
break;
case 'spare':
if (! is_array($value)) {
throw new Exception("log option missing disks array or mirror value");
}
$this->spare = $value;
break;
default:
throw new Exception("$key: Not valid option");
}
} else {
switch ($key) {
case 'atime':
case 'compress':
case 'dedub':
case 'sync':
if ($value != 'on' && $value != 'off') {
throw new Exception("$key: Value must be 'on' or 'off' found '$value'");
}
break;
case 'size':
if (! preg_match('/^\d+(M|G)$/', $value)) {
throw new Exception("$key: Value must have format '100M' or '100G' found '$value'");
}
break;
case 'type':
if (OMVModulePoolType::OMVModulePoolType_TYPE_NONE > $value ||
OMVModulePoolType::OMVModulePoolType_TYPE_RAIDZ3 < $value) {
throw new Exception("$key: Value must be type of OMVPoolType found '$value'");
}
break;
case 'vdevs':
if (! preg_match('/^\d+$/', $value) || $value < 1) {
throw new Exception("$key: Value must be positive int found '$value'");
}
}
}
}
}