]> git.datanom.net - omvzfs.git/blob - src/Zpool.php
Begin to apply correct NotifyListener
[omvzfs.git] / src / Zpool.php
1 <?php
2 require_once('openmediavault/object.inc');
3 require_once('openmediavault/module.inc');
4 require_once("Vdev.php");
5 require_once("Snapshot.php");
6 require_once("Dataset.php");
7 require_once("Zvol.php");
8 require_once("VdevType.php");
9 require_once("Utils.php");
10 require_once("Exception.php");
11
12 /**
13 * Class containing information about the pool
14 *
15 * @author Michael Rasmussen
16 * @version 0.1
17 * @copyright Michael Rasmussen <mir@datanom.net>
18 */
19 class OMVModuleZFSZpool extends OMVModuleAbstract {
20 // Attributes
21 /**
22 * Name of pool
23 *
24 * @var string $name
25 * @access private
26 */
27 private $name;
28
29 /**
30 * List of Vdev
31 *
32 * @var array $vdevs
33 * @access private
34 * @association OMVModuleZFSVdev to vdevs
35 */
36 private $vdevs;
37
38 /**
39 * List of spares
40 *
41 * @var array $spare
42 * @access private
43 * @association OMVModuleZFSVdev to spare
44 */
45 private $spare;
46
47 /**
48 * List of log
49 *
50 * @var array $log
51 * @access private
52 * @association OMVModuleZFSVdev to log
53 */
54 private $log;
55
56 /**
57 * List of cache
58 *
59 * @var array $cache
60 * @access private
61 * @association OMVModuleZFSVdev to cache
62 */
63 private $cache;
64
65 /**
66 * Pool size
67 *
68 * @var int $size
69 * @access private
70 */
71 private $size;
72
73 /**
74 * Pool's mountpoint
75 *
76 * @var string $mountPoint
77 * @access private
78 */
79 private $mountPoint;
80
81 /**
82 * List of features
83 *
84 * @var array $features
85 * @access private
86 */
87 private $features;
88
89 // Associations
90 /**
91 * Array of OMVModuleZFSSnapshot.
92 *
93 * @var array $snapshot
94 * @access private
95 * @association OMVModuleZFSSnapshot to snapshot
96 */
97 private $snapshot;
98
99 /**
100 * Array of OMVModuleZFSDataset
101 *
102 * @var Dataset $dataset
103 * @access private
104 * @association OMVModuleZFSDataset to dataset
105 */
106 private $dataset;
107
108 /**
109 * Array of OMVModuleZFSZvol
110 *
111 * @var Zvol $zvol
112 * @access private
113 * @association OMVModuleZFSZvol to zvol
114 */
115 private $zvol;
116
117 // Operations
118 /**
119 * Constructor
120 *
121 * @param $vdev OMVModuleZFSVdev or array(OMVModuleZFSVdev)
122 * @throws OMVModuleZFSException
123 */
124
125 public function __construct($vdev) {
126 $create_pool = true;
127
128 if (is_array($vdev)) {
129 $cmd = $this->getCommandString($vdev);
130 $name = $vdev[0]->getPool();
131 $type = $vdev[0]->getType();
132 } else if ($vdev instanceof OMVModuleZFSVdev) {
133 $cmd = $this->getCommandString(array($vdev));
134 $name = $vdev->getPool();
135 $type = $vdev->getType();
136 } else {
137 // Assume we make an instance of an existing pool
138 $create_pool = false;
139 }
140
141 $this->vdevs = array();
142 $this->spare = null;
143 $this->log = null;
144 $this->cache = null;
145 $this->features = array();
146 if ($create_pool) {
147 $cmd = "zpool create $name $cmd 2>&1";
148
149 OMVUtil::exec($cmd, $output, $result);
150 if ($result)
151 throw new OMVModuleZFSException(implode("\n", $output));
152 else {
153 $this->name = $name;
154 $this->type = $type;
155 if (is_array($vdev))
156 $this->vdevs = $vdev;
157 else
158 array_push ($this->vdevs, $vdev);
159 $this->size = $this->getAttribute("size");
160 $this->mountPoint = $this->getAttribute("mountpoint");
161 }
162 } else {
163 $this->assemblePool($vdev);
164 }
165 }
166
167 /**
168 * Get pool name
169 *
170 * @return string
171 * @access public
172 */
173 public function getName() {
174 return $this->name;
175 }
176
177 /**
178 * Get array of Vdev
179 *
180 * @return array
181 * @access public
182 */
183 public function getVdevs() {
184 return $this->vdevs;
185 }
186
187 /**
188 * Add Vdev to pool
189 *
190 * @param array $vdev array of OMVModuleZFSVdev
191 * @return void
192 * @throws OMVModuleZFSException
193 * @access public
194 */
195 public function addVdev(array $vdevs) {
196 $cmd = "zpool add " . $this->name . " " . $this->getCommandString($vdevs);
197 OMVUtil::exec($cmd, $output, $result);
198 if ($result)
199 throw new OMVModuleZFSException($output);
200 else
201 $this->vdevs = array_merge($this->vdevs, $vdevs);
202 $this->size = $this->getAttribute("size");
203 }
204
205 /**
206 * XXX
207 *
208 * @param OMVModuleZFSVdev $vdev
209 * @return void
210 * @throws OMVModuleZFSException
211 * @access public
212 */
213 public function removeVdev(OMVModuleZFSVdev $vdev) {
214 throw new OMVModuleZFSException("Cannot remove vdevs from a pool");
215 }
216
217 /**
218 * XXX
219 *
220 * @param OMVModuleZFSVdev $cache
221 * @return void
222 * @throws OMVModuleZFSException
223 * @access public
224 */
225 public function addCache(OMVModuleZFSVdev $cache) {
226 if ($cache->getType() != OMVModuleZFSVdevType::OMVMODULEZFSPLAIN)
227 throw new OMVModuleZFSException("Only a plain Vdev can be added as cache");
228
229 $cmd = "zpool add " . $this->name . " cache " . $this->getCommandString($vdevs);
230 OMVUtil::exec($cmd, $output, $result);
231 if ($result)
232 throw new OMVModuleZFSException($output);
233
234 $disks = $cache->getDisks();
235 foreach ($disks as $disk) {
236 array_push ($this->cache, $disk);
237 }
238 }
239
240 /**
241 * XXX
242 *
243 * @param array $disks
244 * @return void
245 * @throws OMVModuleZFSException
246 * @access public
247 */
248 public function removeCache(array $disks = null) {
249 if (! $disks)
250 $disks = $this->cache;
251
252 foreach ($disks as $disk)
253 $dist_str .= "$disk ";
254
255 $cmd = "zpool remove " . $this->name . " $dist_str";
256 OMVUtil::exec($cmd, $output, $result);
257 if ($result)
258 throw new OMVModuleZFSException($output);
259 else {
260 foreach ($disks as $disk)
261 $this->cache = $this->removeDisk($this->cache, $disk);
262 }
263 }
264
265 /**
266 * XXX
267 *
268 * @return Cache
269 * @access public
270 */
271 public function getCache() {
272 return $this->cache;
273 }
274
275 /**
276 * XXX
277 *
278 * @param OMVModuleZFSVdev $log
279 * @return void
280 * @throws OMVModuleZFSException
281 * @access public
282 */
283 public function addLog(OMVModuleZFSVdev $log) {
284 if ($log->getType() == OMVModuleZFSVdevType::OMVMODULEZFSPLAIN ||
285 $log->getType() == OMVModuleZFSVdevType::OMVMODULEZFSMIRROR) {
286 $cmd = "zpool add " . $this->name . " log " . $this->getCommandString($vdevs);
287 OMVUtil::exec($cmd, $output, $result);
288 if ($result)
289 throw new OMVModuleZFSException($output);
290
291 $this->log = $log;
292 } else
293 throw new OMVModuleZFSException("Only a plain Vdev or mirror Vdev can be added as log");
294 }
295
296 /**
297 * XXX
298 *
299 * @return void
300 * @throws OMVModuleZFSException
301 * @access public
302 */
303 public function removeLog() {
304 foreach ($this->log as $vdev) {
305 if ($vdev->getType() == OMVModuleZFSVdevType::OMVMODULEZFSMIRROR) {
306 $cmd = "zpool remove " . $this->name . " mirror-$i";
307 } else {
308 $disks = $vdev->getDisks();
309 foreach ($disks as $disk)
310 $dist_str .= "$disk ";
311 $cmd = "zpool remove " . $this->name . " $disk_str";
312 }
313 OMVUtil::exec($cmd, $output, $result);
314 if ($result)
315 throw new OMVModuleZFSException($output);
316 else
317 $this->log = array();
318 }
319 }
320
321 /**
322 * XXX
323 *
324 * @return Log
325 * @access public
326 */
327 public function getLog() {
328 return $this->log;
329 }
330
331 /**
332 * XXX
333 *
334 * @param OMVModuleZFSVdev $spares
335 * @return void
336 * @throws OMVModuleZFSException
337 * @access public
338 */
339 public function addSpare(OMVModuleZFSVdev $spares) {
340 if ($spares->getType() != OMVModuleZFSVdevType::OMVMODULEZFSPLAIN)
341 throw new OMVModuleZFSException("Only a plain Vdev can be added as spares");
342
343 $cmd = "zpool add " . $this->name . " spare " . $this->getCommandString($vdevs);
344 OMVUtil::exec($cmd, $output, $result);
345 if ($result)
346 throw new OMVModuleZFSException($output);
347
348 $disks = $spares->getDisks();
349 foreach ($disks as $disk) {
350 array_push ($this->spare, $disk);
351 }
352 }
353
354 /**
355 * XXX
356 *
357 * @param array $disks
358 * @return void
359 * @throws OMVModuleZFSException
360 * @access public
361 */
362 public function removeSpare(array $disks = null) {
363 if (! $disks)
364 $disks = $this->spare;
365
366 foreach ($disks as $disk)
367 $dist_str .= "$disk ";
368
369 $cmd = "zpool remove " . $this->name . " $dist_str";
370 OMVUtil::exec($cmd, $output, $result);
371 if ($result)
372 throw new OMVModuleZFSException($output);
373 else {
374 foreach ($disks as $disk)
375 $this->spare = $this->removeDisk($this->spare, $disk);
376 }
377 }
378
379 /**
380 * XXX
381 *
382 * @return list<Disk>
383 * @access public
384 */
385 public function getSpares() {
386 return $this->spare;
387 }
388
389 /**
390 * XXX
391 *
392 * @return int
393 * @access public
394 */
395 public function getSize() {
396 return $this->size;
397 }
398
399 /**
400 * XXX
401 *
402 * @return string
403 * @access public
404 */
405 public function getMountPoint() {
406 return $this->mountPoint;
407 }
408
409 /**
410 * XXX
411 *
412 * @param array $features
413 * @return void
414 * @throws OMVModuleZFSException
415 * @access public
416 */
417 public function setFeatures(array $features) {
418 foreach ($features as $feature => $value) {
419 $cmd = "zpool set $feature=$value " . $this->name;
420 OMVUtil::exec($cmd, $output, $result);
421 if ($result)
422 throw new OMVModuleZFSException($output);
423 }
424 $this->features = $this->getAllAttributes();
425 }
426
427 /**
428 * We only return array of features for which the user can
429 * change in GUI.
430 *
431 * @return array of features
432 * @access public
433 */
434 public function getFeatures($internal = true) {
435 $attrs = array();
436 $featureSet = array(
437 'recordsize', /* default 131072. 512 <= n^2 <= 131072*/
438 'checksum', /* on | off */
439 'compression', /* off | lzjb | gzip | zle | lz4 */
440 'atime', /* on | off */
441 'aclmode', /* discard | groupmask | passthrough | restricted */
442 'aclinherit', /* discard | noallow | restricted | passthrough | passthrough-x */
443 'casesensitivity', /* sensitive | insensitive | mixed */
444 'primarycache', /* all | none | metadata */
445 'secondarycache', /* all | none | metadata */
446 'logbias', /* latency | throughput */
447 'dedup', /* on | off */
448 'sync' /* standard | always | disabled */
449 );
450 if (count($this->features) < 1)
451 $this->features = $this->getAllAttributes();
452 if ($internal) {
453 foreach ($this->features as $attr => $val) {
454 if (in_array($attr, $featureSet))
455 $attrs[$attr] = $val['value'];
456 }
457 } else {
458 foreach ($this->features as $attr => $val) {
459 if (in_array($attr, $featureSet))
460 $attrs[$attr] = $val;
461 }
462 }
463
464 return $attrs;
465 }
466
467 /**
468 * XXX
469 *
470 * @return void
471 * @throws OMVModuleZFSException
472 * @access public
473 */
474 public function export() {
475 $cmd = "zpool export " . $this->name;
476 OMVUtil::exec($cmd, $output, $result);
477 if ($result)
478 throw new OMVModuleZFSException($output);
479 }
480
481 /**
482 * XXX
483 *
484 * @param string $name
485 * @return void
486 * @throws OMVModuleZFSException
487 * @access public
488 */
489 public function import($name = null) {
490 if ($name)
491 $cmd = "zpool import $name";
492 else
493 $cmd = "zpool import";
494 OMVUtil::exec($cmd, $output, $result);
495 if ($result)
496 throw new OMVModuleZFSException($output);
497 }
498
499 /**
500 * XXX
501 *
502 * @return void
503 * @throws OMVModuleZFSException
504 * @access public
505 */
506 public function scrub() {
507 $cmd = "zpool scrub " . $this->name;
508 OMVUtil::exec($cmd, $output, $result);
509 if ($result)
510 throw new OMVModuleZFSException($output);
511 }
512
513 /**
514 * XXX
515 *
516 * @return string
517 * @throws OMVModuleZFSException
518 * @access public
519 */
520 public function status() {
521 $cmd = "zpool status " . $this->name;
522 OMVUtil::exec($cmd, $output, $result);
523 if ($result)
524 throw new OMVModuleZFSException($output);
525 }
526
527 /**
528 * Get a single property value associated with the Dataset
529 *
530 * @param string $property Name of the property to fetch
531 * @return array The returned array with the property. The property is an associative array with
532 * two elements, <value> and <source>.
533 * @access public
534 */
535 public function getProperty($property) {
536 $attrs = $this->getFeatures(false);
537 return $attrs["$property"];
538 }
539
540 /**
541 * Get an associative array of all properties associated with the Snapshot
542 *
543 * @return array $properties Each entry is an associative array with two elements
544 * <value> and <source>
545 * @access public
546 */
547 public function getProperties() {
548 $attrs = $this->getFeatures(false);
549 return $attrs;
550 }
551
552 /**
553 * Sets a number of Dataset properties. If a property is already set it will be updated with the new value.
554 *
555 * @param array $properties An associative array with properties to set
556 * @return void
557 * @access public
558 */
559 public function setProperties($properties) {
560 foreach ($properties as $newpropertyk => $newpropertyv) {
561 $cmd = "zfs set " . $newpropertyk . "=" . $newpropertyv . " " . $this->name . " 2>&1";
562 OMVModuleZFSUtil::exec($cmd,$out,$res);
563 $attr = $this->getAttribute($newpropertyk);
564 $this->features[$newpropertyk] = $attr;
565 }
566 }
567
568 /**
569 * Destroy the Dataset.
570 *
571 * @return void
572 * @access public
573 */
574 public function destroy() {
575 $cmd = "zpool destroy " . $this->name . " 2>&1";
576 $this->exec($cmd,$out,$res);
577 }
578
579 /**
580 * Clears a previously set proporty and specifies that it should be
581 * inherited from it's parent.
582 *
583 * @param string $property Name of the property to inherit.
584 * @return void
585 * @access public
586 */
587 public function inherit($property) {
588 $cmd = "zfs inherit " . $property . " " . $this->name . " 2>&1";
589 $this->exec($cmd,$out,$res);
590 $attr = $this->getAttribute($newpropertyk);
591 $this->features[$newpropertyk] = $attr;
592 }
593
594 /**
595 * Convert array of Vdev to command string
596 *
597 * @param array $vdevs
598 * @return string
599 * @throws OMVMODULEZFSException
600 */
601 private function getCommandString(array $vdevs) {
602 $adds = array();
603
604 foreach ($vdevs as $vdev) {
605 if (is_object($vdev) == false)
606 throw new OMVMODULEZFSException("Not object of class OMVModuleZFSVdev");
607 if (is_a($vdev, OMVModuleZFSVdev) == false)
608 throw new OMVMODULEZFSException("Object is not of class OMVModuleZFSVdev");
609 $type = $vdev->getType();
610 $command = "";
611
612 switch ($type) {
613 case OMVModuleZFSVdevType::OMVMODULEZFSPLAIN: break;
614 case OMVModuleZFSVdevType::OMVMODULEZFSMIRROR: $command = "mirror"; break;
615 case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1: $command = "raidz1"; break;
616 case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2: $command = "raidz2"; break;
617 case OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3: $command = "raidz3"; break;
618 default:
619 throw new OMVMODULEZFSException("Unknown Vdev type");
620 }
621 $disks = $vdev->getDisks();
622 $diskStr = "";
623 foreach($disks as $disk) {
624 $diskStr .= " $disk";
625 }
626
627 array_push ($adds, $command . $diskStr);
628 }
629
630 return implode(" ", $adds);
631 }
632
633 /**
634 * Get an attribute from pool
635 *
636 * @param string $attribute
637 * @return string value
638 */
639 private function getAttribute($attribute) {
640 $cmd = "zpool list -H -o $attribute {$this->name}";
641 OMVUtil::exec($cmd, $output, $result);
642 if ($result) {
643 $cmd = "zfs list -H -o $attribute {$this->name}";
644 OMVUtil::exec($cmd, $output, $result);
645 if ($result)
646 return null;
647 }
648
649 return $output;
650 }
651
652 /**
653 * Get all attributes from pool
654 * @return array of attributes
655 * @throws OMVModuleZFSException
656 */
657 private function getAllAttributes() {
658 $attrs = array();
659 $cmd = "zfs get -H all {$this->name}";
660
661 try {
662 OMVUtil::exec($cmd, $output, $result);
663 } catch (OMVModuleZFSException $e) {}
664 if ($result)
665 throw new OMVModuleZFSException($output);
666 $output = implode("\n", $output);
667 $res = preg_match_all("/{$this->name}\s+(\w+)\s+([\w\d\.]+)\s+(\w+).*/", $output, $matches, PREG_SET_ORDER);
668 if ($res == false || $res == 0)
669 throw new OMVModuleZFSException("Error return by zpool get all: $output");
670 foreach ($matches as $match) {
671 $attrs[$match[1]] = array('value' => $match[2], 'source' => $match[3]);
672 }
673
674 return $attrs;
675 }
676
677 /**
678 * Get all Dataset properties from commandline and update object properties attribute
679 *
680 * @return void
681 * @access private
682 */
683 private function updateAllProperties() {
684 $this->features = $this->getAllAttributes();
685 }
686
687 /**
688 * Remove a disk from array
689 *
690 * @param array $array
691 * @param string $disk
692 * @return array
693 */
694 private function removeDisk(array $array, $disk) {
695 $new_disks = array();
696
697 foreach ($array as $item) {
698 if (strcmp($item, $disk) != 0)
699 array_push ($new_disks, $item);
700 }
701
702 return $new_disks;
703 }
704
705 /**
706 * Construct existing pool
707 *
708 * @param string $name
709 * @return void
710 * @throws OMVModuleZFSException
711 */
712 private function assemblePool($name) {
713 $cmd = "zpool status -v $name";
714 $types = 'mirror|raidz1|raidz2|raidz3';
715 $dev = null;
716 $type = null;
717 $log = false;
718 $cache = false;
719 $start = true;
720
721 OMVUtil::exec($cmd, $output, $result);
722 if ($result)
723 throw new OMVModuleZFSException($output);
724
725 $this->name = $name;
726 foreach($output as $line) {
727 if (! strstr($line, PHP_EOL))
728 $line .= PHP_EOL;
729 if ($start) {
730 if (preg_match("/^\s*NAME/", $line))
731 $start = false;
732 continue;
733 } else {
734 if (preg_match("/^\s*$/", $line)) {
735 if ($dev) {
736 $this->output($part, $type, $dev);
737 }
738 break;
739 } else if (preg_match("/^\s*($name|logs|cache|spares)/", $line, $match)) {
740 if ($dev) {
741 $this->output($part, $type, $dev);
742 $dev = null;
743 $type = null;
744 }
745 $part = $match[1];
746 } else {
747 switch ($part) {
748 case $name:
749 if (preg_match("/^\s*($types)/", $line, $match)) {
750 /* new vdev */
751 if ($type) {
752 $this->output(null, $type, $dev);
753 $dev = null;
754 }
755 $type = $match[1];
756 } else if (preg_match("/^\s*([\w\d-]+)\s+/", $line, $match)) {
757 if ($dev)
758 $dev .= " $match[1]";
759 else
760 $dev = "$match[1]";
761 }
762 break;
763 case 'logs':
764 if (preg_match("/^\s*([\w\d-]+)\s+/", $line, $match)) {
765 if ($dev)
766 $dev .= " $match[1]";
767 else
768 $dev = "$match[1]";
769 }
770 break;
771 case 'cache':
772 case 'spares':
773 if (preg_match("/^\s*([\w\d-]+)\s+/", $line, $match)) {
774 if ($dev)
775 $dev .= " $match[1]";
776 else
777 $dev = "$match[1]";
778 }
779 break;
780 default:
781 throw new Exception("$part: Unknown pool part");
782 }
783 }
784 }
785 }
786 $this->size = $this->getAttribute("size");
787 $this->mountPoint = $this->getAttribute("mountpoint");
788 }
789
790 /**
791 * Create pool config from parsed input
792 *
793 * @param string $part
794 * @param string $type
795 * @param string $dev
796 * @return void
797 * @throws OMVModuleZFSException
798 */
799 private function output($part, $type, $dev) {
800 $disks = split(" ", $dev);
801 switch ($part) {
802 case 'logs':
803 if ($type && $type != 'mirror')
804 throw new Exception("$type: Logs can only be mirror or plain");
805 if ($type)
806 $this->log = new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSMIRROR, $disks);
807 else
808 $this->log = new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSPLAIN, $disks);
809 break;
810 case 'cache':
811 if ($type)
812 throw new Exception("$type: cache can only be plain");
813 $this->cache = new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSPLAIN, $disks);
814 break;
815 case 'spares':
816 if ($type)
817 throw new Exception("$type: spares can only be plain");
818 $this->spare = new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSPLAIN, $disks);
819 break;
820 default:
821 if ($type) {
822 switch ($type) {
823 case 'mirror':
824 array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSMIRROR, $disks));
825 $this->type = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR;
826 break;
827 case 'raidz1':
828 array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1, $disks));
829 $this->type = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1;
830 break;
831 case 'raidz2':
832 array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2, $disks));
833 $this->type = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2;
834 break;
835 case 'raidz3':
836 array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3, $disks));
837 $this->type = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3;
838 break;
839 }
840 } else {
841 array_push($this->vdevs, new OMVModuleZFSVdev($this->name, OMVModuleZFSVdevType::OMVMODULEZFSPLAIN, $disks));
842 $this->type = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN;
843 }
844 break;
845 }
846 }
847
848 }
849 ?>
This page took 0.250336 seconds and 6 git commands to generate.