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