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