]> git.datanom.net - omvzfs.git/blob - gui/rpc/zfs.inc
Support for cloned filesystems and volumes.
[omvzfs.git] / gui / rpc / zfs.inc
1 <?php
2
3 require_once("openmediavault/object.inc");
4 require_once("openmediavault/config.inc");
5 require_once("openmediavault/error.inc");
6 require_once("openmediavault/util.inc");
7 require_once("openmediavault/rpcservice.inc");
8 require_once("openmediavault/notify.inc");
9 require_once("zfs/Utils.php");
10 require_once("zfs/Dataset.php");
11 require_once("zfs/Snapshot.php");
12 require_once("zfs/Zvol.php");
13 require_once("zfs/Zpool.php");
14 require_once("zfs/NotifyListener.php");
15
16 class OMVRpcServiceZFS extends OMVRpcServiceAbstract {
17 public function getName() {
18 return "ZFS"; // RPC Service name. Same as in .js files
19 }
20
21 /* Initialize the RPC service. Different methods of the RPC service are declared here*/
22 public function initialize() {
23 $this->registerMethod("addPool");
24 $this->registerMethod("getObjectTree");
25 $this->registermethod("passParam");
26 $this->registermethod("addObject");
27 $this->registermethod("deleteObject");
28 $this->registermethod("getProperties");
29 $this->registermethod("setProperties");
30 $this->registermethod("inherit");
31 $this->registermethod("getSharedParams");
32 $this->registermethod("createShare");
33 $this->registermethod("getObjectDetails");
34 $this->registermethod("expandPool");
35 }
36
37 public function addPool($params, $context) {
38 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
39 // Validate the parameters of the RPC service method.
40 $this->validateMethodParams($params, '{
41 "type":"object",
42 "properties":{
43 "pooltype":{"type":"string","enum":["basic","mirror",' .
44 '"raidz1","raidz2","raidz3"]},
45 "force":{"type":"boolean"},
46 "mountpoint":{"type":"string"},
47 "name":{"type":"string"},
48 "devices":{"type":"string"}
49 }
50 }');
51 switch ($params['pooltype']) {
52 case "basic":
53 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN;
54 break;
55 case "mirror":
56 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR;
57 break;
58 case "raidz1":
59 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1;
60 break;
61 case "raidz2":
62 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2;
63 break;
64 case "raidz3":
65 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3;
66 break;
67 default:
68 throw new OMVModuleZFSException("Incorrect pool type specified");
69 break;
70 }
71 //Check for user supplied options
72 $opts = "";
73 if ($params['force']) {
74 $opts .= "-f ";
75 }
76 if (strlen($params['mountpoint']) > 0) {
77 $opts .= "-m " . $params['mountpoint'] . " ";
78 }
79
80 //Use /dev/disk/by-path as deafult when creating new pools as suggested in ZoL FAQ.
81 $disks = preg_split("/[,;]/", $params['devices']);
82 if (file_exists("/dev/disk/by-path/")) {
83 $tmp_disks = array();
84 foreach ($disks as $disk) {
85 $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk);
86 }
87 $disks = $tmp_disks;
88 }
89
90 $vdev = new OMVModuleZFSVdev($params['name'], $pooltype, $disks);
91 $pool = new OMVModuleZFSZpool($vdev, $opts);
92 //Ugly fix to solve the problem of blkid not displaying info on newly created pools
93 $pool->export();
94 $pool->import($pool->getName());
95 }
96
97 public function getObjectTree($params, $context) {
98 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
99 $objects = OMVModuleZFSUtil::getZFSFlatArray();
100 $new = array();
101 foreach ($objects as $a){
102 $new[$a['parentid']][] = $a;
103 }
104 $tree = OMVModuleZFSUtil::createTree($new, $new['root']);
105 OMVModuleZFSUtil::addMissingOMVMntEnt(); //Adds missing ZFS filesystems to the OMV core
106 return $tree;
107 }
108
109 public function passParam($params, $context) {
110 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
111 // Validate the parameters of the RPC service method.
112 $this->validateMethodParams($params, '{
113 "type":"object",
114 "properties":{
115 "key":{"type":"string"},
116 "value":{"type":"string"}
117 }
118 }');
119 return array($params['key'] => $params['value']);
120 }
121
122 public function addObject($params, $context) {
123 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
124 // Validate the parameters of the RPC service method.
125 $this->validateMethodParams($params, '{
126 "type":"object",
127 "properties":{
128 "type":{"type":"string","enum":["filesystem","snapshot",' .
129 '"volume","clone"]},
130 "path":{"type":"string"},
131 "name":{"type":"string"},
132 "size":{"type":"string"},
133 "clonename":{"type":"string"}
134 }
135 }');
136 switch ($params['type']) {
137 case "clone":
138 $tmp = new OMVModuleZFSSnapshot($params['path']);
139 $tmp->clonesnap($params['clonename']);
140 break;
141 case "filesystem":
142 $name = $params['path'] . "/" . $params['name'];
143 $tmp = new OMVModuleZFSDataset($name);
144 break;
145 case "snapshot":
146 $name = $params['path'] . "@" . $params['name'];
147 $tmp = new OMVModuleZFSSnapshot($name);
148 break;
149 case "volume":
150 $name = $params['path'] . "/" . $params['name'];
151 $tmp = new OMVModuleZFSZvol($name);
152 $tmp->create($params['size']);
153 break;
154 default:
155 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
156 break;
157 }
158 }
159
160 public function deleteObject($params, $context) {
161 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
162 // Validate the parameters of the RPC service method.
163 $this->validateMethodParams($params, '{
164 "type":"object",
165 "properties":{
166 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
167 '"Volume","Pool"]},
168 "name":{"type":"string"}
169 }
170 }');
171 global $xmlConfig;
172 $name = $params['name'];
173 switch ($params['type']) {
174 case "Filesystem":
175 OMVModuleZFSUtil::deleteShares($name);
176 $tmp = new OMVModuleZFSDataset($name);
177 $tmp->destroy();
178 break;
179 case "Snapshot":
180 $tmp = new OMVModuleZFSSnapshot($name);
181 $tmp->destroy();
182 break;
183 case "Volume":
184 $tmp = new OMVModuleZFSZvol($name);
185 $tmp->destroy();
186 break;
187 case "Pool":
188 $disks = OMVModuleZFSUtil::getDevDisksByPool($name);
189 $pooluuid = OMVModuleZFSUtil::getUUIDbyName($name);
190 $tmp = new OMVModuleZFSZpool($name);
191 $tmp->destroy();
192 $xpath = "//system/fstab/mntent[fsname='" . $pooluuid . "' and type='zfs']";
193 $object = $xmlConfig->get($xpath);
194 $xmlConfig->delete($xpath);
195 OMVModuleZFSUtil::clearZFSLabel($disks);
196 break;
197 default:
198 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
199 break;
200 }
201 }
202
203 public function getProperties($params, $context) {
204 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
205 // Validate the parameters of the RPC service method.
206 $this->validateMethodParams($params, '{
207 "type":"object",
208 "properties":{
209 "type":{"type":"string"},
210 "name":{"type":"string"},
211 "start":{"type":"integer"},
212 "limit":{'.$GLOBALS['OMV_JSONSCHEMA_COUNTFIELD'].'},
213 "sortfield":{'.$GLOBALS['OMV_JSONSCHEMA_SORTFIELD'].'},
214 "sortdir":{'.$GLOBALS['OMV_JSONSCHEMA_SORTDIR'].'}
215 }
216 }');
217 $objects = array();
218 $name = $params['name'];
219 switch ($params['type']) {
220 case "Filesystem":
221 $tmp = new OMVModuleZFSDataset($name);
222 break;
223 case "Snapshot":
224 $tmp = new OMVModuleZFSSnapshot($name);
225 break;
226 case "Volume":
227 $tmp = new OMVModuleZFSZvol($name);
228 break;
229 case "Pool":
230 $tmp = new OMVModuleZFSZpool($name);
231 break;
232 default:
233 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
234 break;
235 }
236 $properties = $tmp->getProperties();
237 foreach ($properties as $propertyk => $propertyv) {
238 if (!(strcmp($propertyv['source'], "-") == 0)) {
239 $objects[] = array('property' => $propertyk,
240 'value' => $propertyv['value'],
241 'source' => $propertyv['source'],
242 'modified' => "false");
243 }
244 }
245 return $objects;
246 }
247
248 public function setProperties($params, $context) {
249 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
250 // Validate the parameters of the RPC service method.
251 $this->validateMethodParams($params, '{
252 "type":"object",
253 "properties":{
254 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
255 '"Volume","Pool"]},
256 "name":{"type":"string"},
257 "properties":{"type":"array","items":{
258 "type":"object",
259 "properties":{
260 "property":{"type":"string"},
261 "value":{"type":"string"}}}}
262 }
263 }');
264 global $xmlConfig;
265 switch ($params['type']) {
266 case "Filesystem":
267 $tmp = new OMVModuleZFSDataset($params['name']);
268 break;
269 case "Snapshot":
270 $tmp = new OMVModuleZFSSnapshot($params['name']);
271 break;
272 case "Volume":
273 $tmp = new OMVModuleZFSZvol($params['name']);
274 break;
275 case "Pool":
276 $tmp = new OMVModuleZFSZpool($params['name']);
277 break;
278 default:
279 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
280 break;
281 }
282 foreach ($params['properties'] as $property) {
283 unset($objects);
284 $objects = array();
285 $objects[$property['property']] = $property['value'];
286 $tmp->setProperties($objects);
287 if ((strcmp($property['property'], "mountpoint") === 0) && (strcmp($params['type'], "Filesystem") === 0)) {
288 OMVModuleZFSUtil::relocateFilesystem($params['name']);
289 }
290 }
291 }
292
293 public function inherit($params, $context) {
294 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
295 // Validate the parameters of the RPC service method.
296 $this->validateMethodParams($params, '{
297 "type":"object",
298 "properties":{
299 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
300 '"Volume","Pool"]},
301 "name":{"type":"string"},
302 "property":{"type":"string"}
303 }
304 }');
305 // Create a background process.
306 $bgStatusFilename = $this->createBgProcStatus();
307 $pid = $this->fork();
308 if($pid > 0) { // Parent process.
309 $this->initializeBgProcStatus($bgStatusFilename, $pid);
310 return $bgStatusFilename;
311 }
312 // Child process.
313 try {
314 $bgOutputFilename = $this->createBgProcOutput();
315 $this->updateBgProcStatus($bgStatusFilename, "outputfilename", $bgOutputFilename);
316 switch ($params['type']) {
317 case "Filesystem":
318 $tmp = new OMVModuleZFSDataset($params['name']);
319 break;
320 case "Snapshot":
321 $tmp = new OMVModuleZFSSnapshot($params['name']);
322 break;
323 case "Volume":
324 $tmp = new OMVModuleZFSZvol($params['name']);
325 break;
326 case "Pool":
327 $tmp = new OMVModuleZFSZpool($params['name']);
328 break;
329 default:
330 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
331 break;
332 }
333 $tmp->inherit($params['property']);
334 $this->finalizeBgProcStatus($bgStatusFilename, $output);
335 exit(0);
336 } catch(Exception $e) {
337 $this->finalizeBgProcStatus($bgStatusFilename, "", $e);
338 exit(1);
339 }
340 }
341
342 public function getSharedParams($params, $context) {
343 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
344 // Validate the parameters of the RPC service method.
345 $this->validateMethodParams($params, '{
346 "type":"object",
347 "properties":{
348 "type":{"type":"string"},
349 "name":{"type":"string"}
350 }
351 }');
352 $objects = array();
353 //$ds = new OMVModuleZFSDataset($params['name']);
354 //$mountpoint = $ds->getMountPoint();
355 return array(
356 //"mountpoint" => $mountpoint,
357 "name" => $params['name'],
358 "type" => $params['type']);
359 }
360
361 public function createShare($params, $context) {
362 global $xmlConfig;
363 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
364 // Validate the parameters of the RPC service method.
365 $this->validateMethodParams($params, '{
366 "type":"object",
367 "properties":{
368 "name":{"type":"string"},
369 "type":{"type":"string","enum":["Filesystem"]},
370 "sharename":{'.$GLOBALS['OMV_JSONSCHEMA_SHARENAME'].'},
371 "comment":{"type":"string"},
372 "mode":{"type":"string","enum":["700","750","755",'.
373 '"770","775","777"],"optional":true},
374 "mountpoint":{"type":"string"}
375 }
376 }');
377
378 // The field 'reldirpath' may not contain the characters '..'. This
379 // is because of security reasons: the given canonicalized absolute
380 // path MUST be below the given mount point.
381 if(1 == preg_match("/\.\./", $params['mountpoint'])) {
382 throw new OMVException(OMVErrorMsg::E_RPC_SERVICE_METHOD_INVALID_PARAMS,
383 sprintf(gettext("The field '%s' contains forbidden two-dot symbols"), "mountpoint"));
384 }
385
386 // Prepare the configuration object. Use the name of the shared
387 // folder as the relative directory name of the share.
388 switch ($params['type']) {
389 case "Filesystem":
390 $tmp = new OMVModuleZFSDataset($params['name']);
391 break;
392 default:
393 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
394 break;
395 }
396
397 $poolname = OMVModuleZFSUtil::getPoolname($params['name']);
398 $pooluuid = OMVModuleZFSUtil::getUUIDbyName($poolname);
399 $dir = $tmp->getMountPoint();
400
401 //Get the mntent object and fetch it's uuid.
402 $xpath = "//system/fstab/mntent[fsname='" . $pooluuid . "' and dir='" . $dir . "' and type='zfs']";
403 $mountpoint = $xmlConfig->get($xpath);
404 $mntentref = $mountpoint['uuid'];
405
406 $uuid = OMVUtil::uuid();
407 $pathName = $dir . "/" . trim($params['mountpoint'], "/");
408 $reldirpath = trim($params['mountpoint'], "/");
409 $object = array(
410 "uuid" => $uuid,
411 "name" => $params['sharename'],
412 "comment" => $params['comment'] . "*** ZFS share on " . $params['name'] . " ***",
413 "mntentref" => $mntentref,
414 "reldirpath" => $reldirpath
415 );
416
417 // Set the configuration object.
418 $success = FALSE;
419 // Check uniqueness. The share name must be global unique because
420 // the name is also used when exporting a shared folder via NFS for
421 // example.
422 $xpath = sprintf("//system/shares/sharedfolder[name='%s']",
423 $params['name']);
424 if(TRUE === $xmlConfig->exists($xpath)) {
425 throw new OMVException(OMVErrorMsg::E_CONFIG_OBJECT_UNIQUENESS,
426 gettext("A shared folder with the given name already exists"));
427 }
428
429 // Add empty list of privileges per default.
430 $object['privileges'] = array();
431
432 // Append object to configuration.
433 $success = $xmlConfig->set("//system/shares",
434 array("sharedfolder" => $object));
435 if(FALSE === $success) {
436 throw new OMVException(OMVErrorMsg::E_CONFIG_SET_OBJECT_FAILED);
437 }
438
439 // Append the file mode field to the notification object if set.
440 // Defaults to 775.
441 $object['mode'] = "775";
442 if(array_key_exists("mode", $params)) {
443 $object['mode'] = $params['mode'];
444 }
445
446 // Create the shared folder directory if necessary.
447 if(FALSE === file_exists($pathName)) {
448 // Create the directory. Note, the function seems to have a bug
449 // when using the mask parameter. E.g. octdec("777") does not
450 // create the correct permissions as expected, thus change the
451 // mode using chmod.
452 if(FALSE === mkdir($pathName, 0700, TRUE)) {
453 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
454 sprintf("Failed to create the directory '%s'", $pathName));
455 }
456 // Change the directory mode.
457 if(FALSE === chmod($pathName, octdec($object['mode']))) {
458 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
459 sprintf("Failed to set file mode to '%s' for '%s'",
460 $object['mode'], $pathName));
461 }
462 }
463
464 // Change group owner of directory to configured default group,
465 // e.g. 'users'.
466 if(FALSE === chgrp($pathName, $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'])) {
467 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
468 sprintf("Failed to set file group to '%s' for '%s'",
469 $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'], $pathName));
470 }
471
472 // Set the setgid bit. Setting this permission means that all files
473 // created in the folder will inherit the group of the folder rather
474 // than the primary group of the user who creates the file.
475 $mode = fileperms($pathName) | 02000;
476 if(FALSE === chmod($pathName, $mode)) {
477 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
478 sprintf("Failed to set file mode to '%o' for '%s'",
479 $mode, $pathName));
480 }
481
482 // Notify configuration changes.
483 $dispatcher = &OMVNotifyDispatcher::getInstance();
484 $dispatcher->notify(OMV_NOTIFY_CREATE,"org.openmediavault.system.shares.sharedfolder", $object);
485 // Return the configuration object.
486 return $object;
487 }
488
489 public function getObjectDetails($params, $context) {
490 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
491 // Validate the parameters of the RPC service method.
492 $this->validateMethodParams($params, '{
493 "type":"object",
494 "properties":{
495 "name":{"type":"string"},
496 "type":{"type":"string"}
497 }
498 }');
499 $output = "";
500 switch ($params['type']) {
501 case "Filesystem":
502 $output .= "Filesystem details (zfs get all):\n\r\n\r";
503 $cmd = "zfs get all {$params['name']}";
504 break;
505 case "Volume":
506 $output .= "Volume details (zfs get all):\n\r\n\r";
507 $cmd = "zfs get all {$params['name']}";
508 break;
509 case "Snapshot":
510 $output .= "Snapshot details (zfs get all):\n\r\n\r";
511 $cmd = "zfs get all {$params['name']}";
512 break;
513 case "Pool":
514 $output .= "Pool details (zpool get all):\n\r\n\r";
515 $cmd = "zpool get all {$params['name']}";
516 break;
517 default:
518 throw new OMVModuleZFSException("Incorrect type provided");
519 }
520 OMVModuleZFSUtil::exec($cmd,$out,$res);
521 $output .= implode("\n\r", $out);
522 return array("details" => $output);
523 }
524
525 public function expandPool($params, $context) {
526 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
527 // Validate the parameters of the RPC service method.
528 $this->validateMethodParams($params, '{
529 "type":"object",
530 "properties":{
531 "vdevtype":{"type":"string","enum":["basic","mirror",' .
532 '"raidz1","raidz2","raidz3"]},
533 "name":{"type":"string"},
534 "devices":{"type":"string"},
535 "force":{"type":"boolean"}
536 }
537 }');
538 $pool = new OMVModuleZFSZpool($params['name']);
539 switch ($params['vdevtype']) {
540 case "basic":
541 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN;
542 break;
543 case "mirror":
544 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR;
545 break;
546 case "raidz1":
547 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1;
548 break;
549 case "raidz2":
550 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2;
551 break;
552 case "raidz3":
553 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3;
554 break;
555 default:
556 throw new OMVModuleZFSException("Incorrect pool type specified");
557 break;
558 }
559 if ($params['force']) {
560 $opts .= "-f ";
561 }
562 //Use /dev/disk/by-path as deafult when creating new pools as suggested in ZoL FAQ.
563 $disks = preg_split("/[,;]/", $params['devices']);
564 if (file_exists("/dev/disk/by-path/")) {
565 $tmp_disks = array();
566 foreach ($disks as $disk) {
567 $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk);
568 }
569 $disks = $tmp_disks;
570 }
571 $vdev[] = new OMVModuleZFSVdev($params['name'], $pooltype, $disks);
572 $pool->addVdev($vdev, $opts);
573 //Ugly fix to solve the problem of blkid not displaying info on newly created pools
574 $pool->export();
575 $pool->import($pool->getName());
576 }
577 }
578
579 // Register the RPC service.
580 $rpcServiceMgr = &OMVRpcServiceMgr::getInstance(); // Get the "root" instance for the Services
581 $rpcServiceMgr->registerService(new OMVRpcServiceZFS()); // Register a new instance of the RPC service described above
582 ?>
583
This page took 0.106394 seconds and 6 git commands to generate.