5 * Copyright 2013 Michael Rasmussen <mir@datanom.net>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 * @class OMVModuleZPool.
27 require ('OMVModulePoolType.php');
28 require ('OMVModulePoolAction.php');
30 class OMVModuleZPool
{
38 private $status; // one of null, ok, degraded, faulted, scrub, resilver
42 * Constructor of class OMVModuleZPool.
43 * @param params Array which can contain all or a subset of
44 * the following fields:
46 * <li> atime Either true or false. <b>Default is true</b>.
47 * <li> compress Either true or false. <b>Default is false</b>.
48 * <li> dedub Either true or false. <b>Default is false</b>.
49 * <li> name Name for pool
50 * <li> size Size of pool in either MB or GB. 100M or 100G.
51 * <li> sync Either true or false. <b>Default is false</b>.
52 * <li> type See OMVModulePoolType.
53 * <li> vdevs Number of vdevs in pool. <b>Default is 1</b>.
55 * @param options Array which can contain all or a subset of
56 * the following fields:
58 * <li> log Array which contains the following fields:
60 * <li> disks Array of disks. Format: /dev/disk/by-id/.....
61 * <li> mirror Either true or false. <b>Default is false</b>.
63 * <li> cache Array which contains the following fields:
65 * <li> disks Array of disks. Format: /dev/disk/by-id/.... <b>Default use internal</b>.
67 * <li> spare Array of disks. Format: /dev/disk/by-id/...
69 * @param debug Print debug information to STDERR.
72 public function __construct(array $params = array(), array $options = array(), $debug = false) {
73 $this->params
= array(
80 'type' => OMVModulePoolType
::OMVModulePoolType_TYPE_NONE
,
83 $this->debug
= $debug;
85 foreach ($params as $key => $value) {
86 if (array_key_exists($key, $this->params
)) {
87 $this->validate($key, $value);
88 $this->params
[$key] = $value;
90 throw new Exception("$key: Not valid parameter");
94 foreach ($options as $key => $value) {
95 $this->validate($key, $value, true);
101 fprintf(STDERR
, "OMVModuleZPool instantiate with the following params\n");
102 foreach ($this->params
as $key => $value) {
103 $param = ($key == 'type') ? OMVModulePoolType
::toString($value) : $value;
104 fprintf(STDERR
, " %-8s => %s\n", $key, $param);
111 * @param disks Array of disks. Format: /dev/disk/by-id/...
112 * @param name Name for the pool.
113 * @param options Array which can contain all or a subset of
114 * the following fields:
116 * <li> log Array which contains the following fields:
118 * <li> disks Array of disks. Format: /dev/disk/by-id/...
119 * <li> mirror Either true or false. <b>Default is false</b>.
121 * <li> cache Array which contains the following fields:
123 * <li> disks Array of disks. Format: /dev/disk/by-id/... <b>Default use internal</b>.
125 * <li> spare Array of disks. Format: /dev/disk/by-id/...
129 public function createPool(array $disks, $name = null, array $options = array()) {
130 return $this->updatePool(OMVModulePoolAction
::OMVModulePoolAction_TYPE_CREATE
,
131 $disks, count($disks), $name, $options);
136 * @param disks Array of disks. Format: /dev/disk/by-id/...
137 * @param name Name for the pool.
138 * @param options Array which can contain all or a subset of
139 * the following fields:
141 * <li> log Array which contains the following fields:
143 * <li> disks Array of disks. Format: /dev/disk/by-id/...
144 * <li> mirror Either true or false. <b>Default is false</b>.
146 * <li> cache Array which contains the following fields:
148 * <li> disks Array of disks. Format: /dev/disk/by-id/... <b>Default use internal</b>.
150 * <li> spare Array of disks. Format: /dev/disk/by-id/...
154 public function addPool(array $disks, $name = null, array $options = array()) {
155 $this->initPool($name);
156 return $this->updatePool(OMVModulePoolAction
::OMVModulePoolAction_TYPE_ADD
,
157 $disks, count($disks), $name, $options);
160 public function removeDevice(array $disks, $name = null) {
163 public function destroyPool($name = null) {
166 public function scrubPool($name = null) {
169 public function exportPool($name = null){
172 public function importPool($name = null){
176 * public function attachDevice($extingDisk, $newDisk, $name = null)
177 * public function detachDevice($disk, $name = null)
178 * public function onlineDevice($disk, $name = null)
179 * public function offlineDevice($disk, $name = null)
180 * public function replaceDevice($extingDisk, $newDisk, $name = null)
181 * public function upgradePool($name = null)
184 private function initPool($name = null) {
185 // Replace with OMVUtil::exec
186 $this->validateName($name);
189 private function validateName($name = null) {
192 $oldname = $this->params
['name'];
193 $this->params
['name'] = $name;
195 if (! $this->params
['name'] ||
! preg_match('/^\w+$/', $this->params
['name'])) {
197 $this->params
['name'] = $oldname;
199 throw new Exception("Name of pool cannot be null or empty");
205 * @param action Action to be taken. create or add.
206 * @param disks Array of disks. Format: /dev/disk/by-id/...
207 * @param disk_num Number of disk(s) in the pool.
208 * @param name Name for the pool.
209 * @param options Array which can contain all or a subset of
210 * the following fields:
212 * <li> log Array which contains the following fields:
214 * <li> disks Array of disks. Format: /dev/disk/by-id/...
215 * <li> mirror Either true or false. <b>Default is false</b>.
217 * <li> cache Array which contains the following fields:
219 * <li> disks Array of disks. Format: /dev/disk/by-id/... <b>Default use internal</b>.
221 * <li> spare Array of disks. Format: /dev/disk/by-id/...
225 private function updatePool($action, array $disks, $disk_num, $name = null, array $options = array()) {
226 OMVModulePoolAction
::toString($action);
227 $cmd = 'zpool ' . OMVModulePoolAction
::getAction($action);
229 throw new Exception("Pool must have at least 1 disk");
231 $this->validateName($name);
232 foreach ($options as $key => $value) {
233 $this->validate($key, $value, true);
236 case OMVModulePoolAction
::OMVModulePoolAction_TYPE_CREATE
:
237 $cmd .= ' ' . $this->params
['name'];
239 case OMVModulePoolAction
::OMVModulePoolAction_TYPE_ADD
:
240 $cmd .= ' ' . $this->params
['name'];
243 switch ($this->params
['type']) {
244 case OMVModulePoolType
::OMVModulePoolType_TYPE_NONE
:
245 print "Create basic pool\n";
246 if ($this->params
['vdevs'] != 1) {
247 throw new Exception("A basic zpool can only have 1 vdev");
250 foreach ($disks as $disk) {
251 $vdev .= ($vdev) ?
" $disk" : "$disk";
255 case OMVModulePoolType
::OMVModulePoolType_TYPE_MIRROR
:
256 print "Create mirrored pool\n";
257 if ($disk_num / $this->params
['vdevs'] < 2 ||
258 ($disk_num / $this->params
['vdevs']) %
$this->params
['vdevs']) {
259 throw new Exception("$disk_num disk(s) cannot be evenly distributed to ".
260 $this->params
['vdevs']." vdev(s) and form a proper stripe or mirror");
263 $disk_sum = $disk_num / $this->params
['vdevs'];
265 for ($i = 1, $num = 0; $i <= $disk_num; $i++
) {
266 $s .= ($s) ?
" ${disks[$i-1]}" : "${disks[$i-1]}";
267 if ($i %
$disk_sum == 0) {
272 foreach ($vdevs as $vdev) {
273 $cmd .= " mirror $vdev";
276 case OMVModulePoolType
::OMVModulePoolType_TYPE_RAIDZ1
:
277 print "Create raidz1 pool\n";
278 if ($disk_num / $this->params
['vdevs'] < 3 ||
279 ($disk_num / $this->params
['vdevs']) %
$this->params
['vdevs']) {
280 throw new Exception("$disk_num disk and ".$this->params
['vdevs'].
281 " vdev(s) cannot provide mininum 3 disks per vdev for raidz1");
284 $disk_sum = $disk_num / $this->params
['vdevs'];
286 for ($i = 1, $num = 0; $i <= $disk_num; $i++
) {
287 $s .= ($s) ?
" ${disks[$i-1]}" : "${disks[$i-1]}";
288 if ($i %
$disk_sum == 0) {
293 foreach ($vdevs as $vdev) {
294 $cmd .= " raidz1 $vdev";
297 case OMVModulePoolType
::OMVModulePoolType_TYPE_RAIDZ2
:
298 print "Create raidz2 pool\n";
299 if ($disk_num / $this->params
['vdevs'] < 4 ||
300 ($disk_num / $this->params
['vdevs']) %
$this->params
['vdevs']) {
301 throw new Exception("$disk_num disk and ".$this->params
['vdevs'].
302 " vdev(s) cannot provide mininum 4 disks per vdev for raidz2");
305 $disk_sum = $disk_num / $this->params
['vdevs'];
307 for ($i = 1, $num = 0; $i <= $disk_num; $i++
) {
308 $s .= ($s) ?
" ${disks[$i-1]}" : "${disks[$i-1]}";
309 if ($i %
$disk_sum == 0) {
314 foreach ($vdevs as $vdev) {
315 $cmd .= " raidz2 $vdev";
318 case OMVModulePoolType
::OMVModulePoolType_TYPE_RAIDZ3
:
319 print "Create raidz3 pool\n";
320 if ($disk_num / $this->params
['vdevs'] < 5 ||
321 ($disk_num / $this->params
['vdevs']) %
$this->params
['vdevs']) {
322 throw new Exception("$disk_num disk and ".$this->params
['vdevs'].
323 " vdev(s) cannot provide mininum 5 disks per vdev for raidz3");
326 $disk_sum = $disk_num / $this->params
['vdevs'];
328 for ($i = 1, $num = 0; $i <= $disk_num; $i++
) {
329 $s .= ($s) ?
" ${disks[$i-1]}" : "${disks[$i-1]}";
330 if ($i %
$disk_sum == 0) {
335 foreach ($vdevs as $vdev) {
336 $cmd .= " raidz3 $vdev";
340 foreach ($options as $key => $value) {
344 if ($value['mirror'] === true) {
347 foreach ($value['disks'] as $disk) {
353 foreach ($value as $disk) {
359 foreach ($value as $disk) {
366 fprintf(STDERR
, "%s\n", $cmd);
374 * @param key Option to validate.
375 * @param value Value for option to validate.
376 * @param type See OMVModulePoolType.
377 * @throws Exception if option is invalid.
379 private function validate($key, $value, $options = false) {
383 if (! is_array($value) ||
! array_key_exists('mirror', $value) ||
384 ! array_key_exists('disks', $value)) {
385 throw new Exception("log option missing disks array or mirror value");
387 if ($value['mirror'] === true && count($value['disks']) != 2) {
388 throw new Exception("Two disks is required to form a log mirror");
390 $this->log
= $value['disks'];
391 $this->mirror_log
= $value['mirror'];
394 if (! is_array($value)) {
395 throw new Exception("log option missing disks array or mirror value");
397 $this->cache
= $value;
400 if (! is_array($value)) {
401 throw new Exception("log option missing disks array or mirror value");
403 $this->spare
= $value;
406 throw new Exception("$key: Not valid option");
414 if ($value != 'on' && $value != 'off') {
415 throw new Exception("$key: Value must be 'on' or 'off' found '$value'");
419 if (! preg_match('/^\d+(M|G)$/', $value)) {
420 throw new Exception("$key: Value must have format '100M' or '100G' found '$value'");
424 if (OMVModulePoolType
::OMVModulePoolType_TYPE_NONE
> $value ||
425 OMVModulePoolType
::OMVModulePoolType_TYPE_RAIDZ3
< $value) {
426 throw new Exception("$key: Value must be type of OMVPoolType found '$value'");
430 if (! preg_match('/^\d+$/', $value) ||
$value < 1) {
431 throw new Exception("$key: Value must be positive int found '$value'");