1 <?php
3 class faiDiskEntry extends plugin
4 {
5 /* attribute list for save action */
6 var $ignore_account= TRUE;
7 var $attributes = array("DISKcn","DISKdescription");
8 var $UsedAttrs = array("cn","description","FAIpartitionType",
9 "FAIpartitionNr","FAIfsType","FAImountPoint","FAIpartitionSize",
10 "FAIfsTuneOptions", "FAIfsCreateOptions", "FAImountOptions",
11 "FAIfsOptions","FAIpartitionFlags","FAIdiskOption");
13 var $objectclasses= array();
15 var $DISKcn = "";
16 var $DISKdescription = "";
17 var $DISKFAIdiskOptions = "";
19 var $partitions = array();
20 var $is_edit = false;
21 var $old_cn = "";
22 var $fstabkey = "device";
23 var $disklabel = "msdos";
24 var $FAIstate = "";
25 var $FAIdiskType = "disk";
27 var $lvmDevices = array();
29 function faiDiskEntry (&$config, $dn= NULL,$parent,$disk,$type)
30 {
31 plugin::plugin ($config, $dn);
33 // Set default attributes
34 $this->parent = $parent;
35 $this->FAIdiskType = $type;
37 // If disk is not empty, then we are going to edit
38 // an existing disk, load disk info now.
39 if($disk){
41 // Get devices used in volume base disks.
42 if(isset($disk['FAIlvmDevice'])){
43 $this->lvmDevices = $disk['FAIlvmDevice'];
44 }
46 // Load default attributes
47 $this->DISKcn = $disk['cn'];
48 $this->DISKdescription = $disk['description'];
49 $this->is_edit = true;
50 $this->old_cn = $disk['cn'];
52 // Load partition info
53 foreach($disk['partitions'] as $values){
55 $name = $values['FAIpartitionNr'];
57 // Load partition attributes
58 $disk['partitions'][$name]['old_cn']= $disk['partitions'][$name]['cn'];
59 $disk['partitions'][$name]['FAIdiskType']= $this->FAIdiskType;
60 foreach($this->UsedAttrs as $attr){
61 if(!isset($values[$attr])){
62 $disk['partitions'][$name][$attr]="";
63 }
64 }
66 if (preg_match('/^_/', $disk['partitions'][$name]['FAIfsType'])){
67 $disk['partitions'][$name]['FAIfsType']=
68 preg_replace('/^_/', '', $disk['partitions'][$name]['FAIfsType']);
69 }
70 }
72 $this->partitions = $disk['partitions'];
74 /* Load FAIdiskOptions.
75 * Some options are disk related and others are used for partitions.
76 * - fstabkey -> disk
77 * - disklabel -> disk
78 * - bootable -> partition
79 * - preserve -> partition
80 * - resize -> partition
81 */
82 if (isset($disk['FAIdiskOption'])){
83 foreach($disk['FAIdiskOption'] as $option) {
85 // Get fstab key
86 if (preg_match("/fstabkey:(device|label|uuid)/", $option)){
87 $this->fstabkey= preg_replace("/^.*fstabkey:(device|label|uuid).*$/", "$1", $option);
88 continue;
89 }
91 // Get disk label
92 if (preg_match("/disklabel:(msdos|gpt)/", $option)){
93 $this->disklabel= preg_replace("/^.*disklabel:(msdos|gpt).*$/", "$1", $option);
94 continue;
95 }
97 // Load bootable flag for partitions
98 if (preg_match("/^bootable:/", $option)){
99 $bootable = explode(",", trim(preg_replace("/^bootable:/","",$option),","));
100 foreach($bootable as $bootflag){
101 if(isset($this->partitions[$bootflag])){
102 $this->partitions[$bootflag]['bootable'] = TRUE;
103 }
104 }
105 continue;
106 }
108 // Load resize flag for partitions
109 if (preg_match("/^resize:/", $option)){
110 $resize = explode(",", trim(preg_replace("/^resize:/","",$option),","));
111 foreach($resize as $id){
112 if(isset($this->partitions[$id])){
113 $this->partitions[$id]['resize'] = TRUE;
114 }
115 }
116 continue;
117 }
119 // Load preserve_always flag for partitions
120 if (preg_match("/^preserve_always:/", $option)){
121 $preserve = explode(",", trim(preg_replace("/^preserve_always:/","",$option),","));
122 foreach($preserve as $presflag){
123 if(isset($this->partitions[$presflag])){
124 $this->partitions[$presflag]['preserve'] = TRUE;
125 $this->partitions[$presflag]['preserveType'] = 'always';
126 }
127 }
128 continue;
129 }
131 // Load preserve_reinstall flag for partitions
132 if (preg_match("/^preserve_reinstall:/", $option)){
133 $preserve = explode(",", trim(preg_replace("/^preserve_reinstall:/","",$option),","));
134 foreach($preserve as $presflag){
135 if(isset($this->partitions[$bootflag])){
136 $this->partitions[$presflag]['preserve'] = TRUE;
137 $this->partitions[$presflag]['preserveType'] = 'reinstall';
138 }
139 }
140 continue;
141 }
142 }
143 } else {
144 $this->fstabkey= "device";
145 }
146 }
148 // Prepare lists
149 $this->diskList = new sortableListing();
150 $this->diskList->setDeleteable(true);
151 $this->diskList->setEditable(true);
152 $this->diskList->setWidth("100%");
153 $this->diskList->setHeight("400px");
154 $this->diskList->setColspecs(array('200px','*'));
155 $this->diskList->setHeader(array("Name",_("Description"),_("Type"),_("Mount point"),_("Options")));
156 $this->diskList->setDefaultSortColumn(1);
157 $this->diskList->setAcl('rwcdm'); // All ACLs, we filter on our own here.
158 }
161 function execute()
162 {
163 /* Call parent execute */
164 plugin::execute();
166 // Fill templating stuff
167 $smarty = get_smarty();
168 $s_action = "";
169 $s_entry = "";
170 $display = "";
172 // Add partition to lvm compilation.
173 if(isset($_POST['addLvmPartition']) && isset($_POST['lvmPartitionAdd'])){
174 $name = get_post('lvmPartitionAdd');
175 $this->lvmDevices[$name] = $name;
176 }
178 // Remove partition from lvm compilation.
179 if(isset($_POST['delLvmPartition']) && isset($_POST['physicalPartition'])){
180 $names = get_post('physicalPartition');
181 foreach($names as $name){
182 if(isset($this->lvmDevices[$name])){
183 unset($this->lvmDevices[$name]);
184 }
185 }
186 }
188 // Get list actions
189 $this->diskList->save_object();
190 $action = $this->diskList->getAction();
191 if($action['action'] == 'edit'){
192 $id = $this->diskList->getKey($action['targets'][0]);
193 if(isset($this->partitions[$id])){
194 $this->dialog = new faiPartition($this->config,$this->partitions[$id], $this,$this->FAIdiskType);
195 }
196 }
197 if($action['action'] == 'delete'){
198 $id = $this->diskList->getKey($action['targets'][0]);
199 if(isset($this->partitions[$id])){
200 $this->removePartition($id);
201 }
202 }
204 /* Create a new partition for this disk.
205 */
206 if(isset($_POST['AddPartition']) && !preg_match("/freeze/i",$this->FAIstate)){
207 $this->dialog = new faiPartition($this->config, array(), $this,$this->FAIdiskType);
208 }
210 /* Handle partition dialogs.
211 */
212 if($this->dialog instanceOf plugin && isset($_POST['PartitionCancel'])){
213 $this->dialog = null;
214 }
215 if($this->dialog instanceOf plugin && isset($_POST['PartitionSave'])){
216 $this->dialog->save_object();
218 // Validate new partition
219 $new_partition = $this->dialog->save();
220 $msgs = $this->dialog->check();
221 $msgs = array_merge($msgs,$this->check_disks($new_partition));
223 if(!count($msgs)){
224 $this->updatePartition($new_partition);
225 $this->dialog = null;
226 }else{
227 msg_dialog::displayChecks($msgs);
228 }
229 }
230 if($this->dialog instanceOf plugin){
231 $this->dialog->save_object();
232 return($this->dialog->execute());
233 }
235 // Assign checkbox related values.
236 foreach($this->attributes as $attrs){
237 $smarty->assign($attrs,set_post($this->$attrs));
238 if($this->$attrs){
239 $smarty->assign($attrs."CHK"," ");
240 }else{
241 $smarty->assign($attrs."CHK"," disabled ");
242 }
243 }
245 // Assign disk info to smarty.
246 $smarty->assign("setup", $this->generateParts());
247 $smarty->assign("sub_object_is_createable",$this->acl_is_createable());
248 $smarty->assign("freeze",preg_match("/freeze/i",$this->FAIstate));
249 $smarty->assign("fstabkeys", array("device" => _("Device"), "label" => _("Label"), "uuid" => _("UUID")));
250 $smarty->assign("disklabels", array("msdos" => "MSDOS", "gpt" => "GPT"));
251 $smarty->assign("fstabkey", $this->fstabkey);
252 $smarty->assign("disklabel", set_post($this->disklabel));
253 $smarty->assign("FAIdiskType", $this->FAIdiskType);
254 $smarty->assign("plist", $this->getPartitionList());
255 $smarty->assign("physicalPartitionList", $this->getAvailablePartitions());
257 // Assign partitions
258 $tmp = $this->plInfo();
259 $sacl = "";
260 foreach($tmp['plProvidedAcls'] as $name => $translated){
261 $acl = $this->getacl($name, preg_match("/freeze/i",$this->FAIstate));
262 $smarty->assign($name."ACL",$acl);
263 }
265 $display.= $smarty->fetch(get_template_path('faiDiskEntry.tpl', TRUE));
266 return($display);
267 }
270 /* Creates a human readable list, that contains all physical
271 * devices that are used by the volume group.
272 * This list will then be displayed in a html select box.
273 * (lvm)
274 */
275 function getPartitionList()
276 {
277 /* Create a list of all available disks and partitions.
278 * This list will then be used to display detailed info.
279 */
280 $disks = $this->parent->disks;
281 foreach($disks as $dname => $disk){
283 // Skip currently edited disk
284 if($disk['cn'] == $this->old_cn) continue;
286 // Add disk
287 $objs[$dname] = $disk;
289 // Add disk partitions
290 foreach($disk['partitions'] as $id => $part){
291 $part['parentDisk'] = $disk;
292 $objs[$part['cn']] = $part;
293 }
294 }
296 // Attach current disk setup to the details list.
297 $data = $this->save();
298 $objs[$data['cn']] = $data;
299 foreach($data['partitions'] as $part){
300 $part['parentDisk'] = $data;
301 $objs[$part['cn']] = $part;
302 }
304 // Walk through physical partition combinations and build up
305 // user friendly list with partition details.
306 $list = array();
307 foreach($this->lvmDevices as $device){
309 // We've a html select box here, add spaces for better readability
310 $str = $device;
311 $str = preg_replace("/ /"," ",str_pad($str,20," "));
313 // Add disk/partition details.
314 if(isset($objs[$device])){
315 if(isset($objs[$device]['FAIpartitionSize'])){
316 if($objs[$device]['parentDisk']['FAIdiskType'] == "raid"){
317 $str .= _("Disks").": ";
318 $str .= preg_replace("/(:spare|:missing)/i","",$objs[$device]['FAIpartitionSize']);
319 }else{
320 $str .= _("Size").": ";
321 $str .= $objs[$device]['FAIpartitionSize'];
322 }
323 }
324 }
325 $list[$device] = $str;
326 }
327 return($list);
328 }
331 /* Returns a list of available partitions that are useable in
332 * lvm disk setups.
333 */
334 function getAvailablePartitions()
335 {
336 $may = $used = array();
337 foreach($this->parent->disks as $disk){
339 // Skip ourselves
340 if($disk['cn'] == $this->DISKcn) continue;
342 // Add partition from lvm combinations
343 if($disk['FAIdiskType'] == "lvm"){
344 $used = array_merge($used,$disk['FAIlvmDevice']);
345 }
347 foreach($disk['partitions'] as $key => $part){
349 // Add disks of raid arrays, to the used list.
350 if($disk['FAIdiskType'] == "raid"){
351 foreach(explode(",",$part['FAIpartitionSize']) as $rDevice){
352 $used[] = preg_replace("/:.*$/i","",$rDevice);
353 }
354 }
356 // Collect all available partitions
357 if($disk['FAIdiskType'] == "disk" || $disk['FAIdiskType'] == "raid"){
358 $name = $part['cn'];
359 if(!isset($this->lvmDevices[$name])){
360 $may[] = $name;
361 }
362 }
363 }
364 }
366 // Check which of the available disks are unused.
367 $ret = array();
368 foreach($may as $val){
369 if(!in_array_strict($val,$used)){
370 $ret[$val] = $val;
371 }
372 }
373 return($ret);
374 }
377 /* Remove the selected partition and shift the following partitions
378 * to fill the gap.
379 * Additionally update the partition numbers correspondingly.
380 * (Checks if the partition is in use, too)
381 */
382 function removePartition($id)
383 {
384 $start = false;
386 /* Create a list of all partitions that are used in
387 * lvm or raid compilations.
388 */
389 $list = array();
390 foreach($this->parent->disks as $dname => $disk){
391 if($disk['FAIdiskType'] != "disk"){
392 if($disk['FAIdiskType'] == "lvm"){
393 foreach($disk['FAIlvmDevice'] as $partname){
394 $list[preg_replace("/:.*$/","",$partname)][] = $disk;
395 }
396 }
397 foreach($disk['partitions'] as $partkey => $part){
398 if($disk['FAIdiskType'] == "raid"){
399 foreach(explode(",",$part['FAIpartitionSize']) as $partname){
400 $list[preg_replace("/:.*$/","",$partname)][] = $disk;
401 }
402 }
403 }
404 }
405 }
407 /* Now that we've a list of all partition references, lets check if
408 * one of the partitions we are going to remove is still in use.
409 */
410 if(isset($list[$this->partitions[$id]['cn']])){
411 $used = array();
412 foreach($list[$this->partitions[$id]['cn']] as $disk){
413 $used[$disk['cn']] = $disk['cn'];
414 }
415 $used = implode(",",$used);
416 msg_dialog::display(_("Error"),
417 sprintf(_("The disk cannot be deleted while it is used in the '%s' disk definition!"),
418 $used), ERROR_DIALOG);
419 }else{
420 unset($this->partitions[$id]);
421 }
422 }
425 function get_free_partition_number()
426 {
427 $used = array();
428 foreach($this->partitions as $key => $part){
429 $used[$key] = $part['FAIpartitionNr'];
430 }
431 $id = 1;
432 while(in_array_strict($id,$used) && $id < 16 ){
433 $id++;
434 }
435 return($id);
436 }
440 /* Add or update a partition
441 */
442 function updatePartition($part)
443 {
444 if(!isset($part['FAIpartitionNr']) || $part['FAIpartitionNr'] == "undefined"){
445 $part['FAIpartitionNr'] = $this->get_free_partition_number();
446 }
448 /* Update the disk cn -
449 * Do NOT touch the partition 'cn' in case of lvm or raid devices.
450 */
451 if($this->FAIdiskType == "disk"){
452 $part['cn'] = $this->DISKcn.$part['FAIpartitionNr'];
453 }
455 /* Check if we've to update partition names of lvm compilations.
456 */
457 if($this->FAIdiskType == "lvm"){
458 if(isset($this->partitions[$part['FAIpartitionNr']])){
459 $old_cn = $this->partitions[$part['FAIpartitionNr']]['cn'];
460 $new_cn = $part['cn'];
461 if(isset($this->lvmDevices[$old_cn])){
462 unset($this->lvmDevices[$old_cn]);
463 $this->lvmDevices[$new_cn] = $new_cn;
464 }
465 }
466 }
468 /* Set raid names to md#
469 */
470 if($this->FAIdiskType == "raid"){
471 $part['cn'] = 'md'.$part['FAIpartitionNr'];
472 }
474 $this->partitions[$part['FAIpartitionNr']] = $part;
475 }
478 /* This method generates the partition listing with all necessary info,
479 * depending on the disk type.
480 */
481 function generateParts()
482 {
483 $data = $lData = array();
484 foreach($this->partitions as $key => $part){
486 $cn = $part['cn'];
487 $desc = $part['description'];
488 $number = $part['FAIpartitionNr'];
489 $size = $part['FAIpartitionSize'];
490 $type = $part['FAIpartitionType'];
492 // Remove encryption info from the mount point.
493 $mnt = $part['FAImountPoint'];
494 if(preg_match("/:encrypt/", $mnt)){
495 $mnt = preg_replace("/:encrypt/","",$mnt);
496 }
498 // create human readable strings out of the flags.
499 $opt = "";
500 if(isset($part['encrypted']) && $part['encrypted']){
501 $opt.= " "._("encrypted").", ";
502 }
503 if(isset($part['bootable']) && $part['bootable']){
504 $opt.= " "._("boot able").", ";
505 }
506 if(isset($part['preserve']) && $part['preserve']){
507 $opt.= " "._("preserve").": ".$part['preserveType'].", ";
508 }
510 // Combine options to a single table col.
511 $opt = "<i>".preg_replace('/, $/',"",$opt)." </i>";
513 // Build up info table, depending on the disk type.
514 $data[$key]=$key;
515 if($this->FAIdiskType == "lvm"){
516 $lData[$key]=array('data' => array($cn,$desc,"",$mnt,$opt,$size));
517 }elseif($this->FAIdiskType == "raid"){
518 $raid = $part['FAIpartitionType']." (".$part['FAIpartitionSize'].")";
519 $lData[$key]=array('data' => array($cn,$desc,$raid,$mnt,$opt));
520 }else{
521 $lData[$key]=array('data' => array("",$desc,$type,$mnt,$opt,$size));
522 }
523 }
524 $this->diskList->setListData($data,$lData);
525 $acl = "rwcdm";
526 if(preg_match("/freeze/", $this->FAIstate)) $acl = "r";
527 $this->diskList->setAcl($acl);;
528 $this->diskList->update();
529 return($this->diskList->render());
530 }
533 function save()
534 {
535 $tmp = array();
536 $tmp['cn'] = $this->DISKcn;
538 /* Attach partitions.
539 * And prepare names and numbers.
540 */
541 foreach($this->partitions as $key=>$val) {
542 $this->partitions[$key]['FAIpartitionNr']=$key;
543 if($this->FAIdiskType == "disk"){
544 $this->partitions[$key]['cn'] = $this->DISKcn.$key;
545 }elseif($this->FAIdiskType == "lvm"){
546 $this->partitions[$key]['FAIpartitionType'] = 'lvm';
547 }
548 }
550 $tmp['description'] = $this->DISKdescription;
551 $tmp['partitions'] = $this->partitions;
552 $tmp['FAIdiskType'] = $this->FAIdiskType;
554 // Add lvm devices if available.
555 $tmp['FAIlvmDevice'] = array();
556 foreach($this->lvmDevices as $dev){
557 $tmp['FAIlvmDevice'][] = $dev;
558 }
560 /* Assemble flags */
561 $tmp['FAIdiskOption'] = array("fstabkey:".$this->fstabkey, "disklabel:".$this->disklabel);
563 /* If hdd name has changed, tell partitionTable to rename it */
564 if(($this->is_edit)&&($this->old_cn != $this->DISKcn)){
565 $tmp['rename']['from'] = $this->old_cn;
566 $tmp['rename']['to'] = $this->DISKcn;
567 }
569 // Build up disk options
570 $bootable = "";
571 $resize = "";
572 $preserve_always = "";
573 $preserve_reinstall = "";
575 /* Assemble boot, resize and preserve flags
576 */
577 foreach($tmp['partitions'] as $id => $part){
578 if(isset($part['bootable']) && $part['bootable']){
579 $bootable .= $id.",";
580 }
581 if(isset($part['resize']) && $part['resize']){
582 $resize .= $id.",";
583 }
584 if(isset($part['preserve']) && $part['preserve']){
585 if($part['preserveType'] == "always"){
586 $preserve_always .= $id.",";
587 }else{
588 $preserve_reinstall .= $id.",";
589 }
590 }
592 // Unset non valid attributes
593 foreach(array("bootable","encrypted","preserve","preserveType","resize","FAIdiskType") as $attr){
594 if(isset($tmp['partitions'][$id][$attr])){
595 unset($tmp['partitions'][$id][$attr]);
596 }
597 }
598 }
600 /* Assembe disk flags
601 */
602 if(!empty($bootable)){
603 $tmp['FAIdiskOption'][] = "bootable:".trim($bootable,",");
604 }
605 if(!empty($resize)){
606 $tmp['FAIdiskOption'][] = "resize:".trim($resize,",");
607 }
608 if(!empty($preserve_always)){
609 $tmp['FAIdiskOption'][] = "preserve_always:".trim($preserve_always,",");
610 }
611 if(!empty($preserve_reinstall)){
612 $tmp['FAIdiskOption'][] = "preserve_reinstall:".trim($preserve_reinstall,",");
613 }
615 return($tmp);
616 }
619 /* Save data to object */
620 function save_object()
621 {
622 if((isset($_POST['TableEntryFrameSubmitted'])) && !preg_match("/freeze/", $this->FAIstate) ){
623 plugin::save_object();
625 // Save posted disk label and fstab key
626 if (isset($_POST['disklabel']) && preg_match("/^(msdos|gpt)$/", $_POST['disklabel'])){
627 $this->disklabel= get_post('disklabel');
628 }
629 if (isset($_POST['fstabkey']) && preg_match("/^(device|label|uuid)$/", $_POST['fstabkey'])){
630 $this->fstabkey= get_post('fstabkey');
631 }
632 }
633 }
636 /* Check supplied data */
637 function check()
638 {
639 /* Call common method to give check the hook */
640 $message= plugin::check();
642 /* Check for an empty disk name */
643 $d = trim($this->DISKcn);
644 if($d == "" ){
645 $message[] = msgPool::required(_("Name"));
646 }
647 if(preg_match("/[^a-z0-9_\-]/i",$d)){
648 $message[] = msgPool::invalid(_("Name"),$d,"/[a-z0-9_\-]/i");
649 }
651 return ($message);
652 }
655 /* Checks the disk combinations.
656 *
657 */
658 function check_disks($disk_to_add = array())
659 {
660 $msgs = array();
662 /* Check 'disk' combinations.
663 * - There can be four primary partitions.
664 * - If there is at least one 'logical' partition, then there can be only
665 * three 'primary' partitions.
666 */
667 if($this->FAIdiskType == "disk"){
669 $types = array('logical' => array(), 'primary' => array());
670 $types[$disk_to_add['FAIpartitionType']][$disk_to_add['FAIpartitionNr']] = 1;
671 foreach($this->partitions as $key => $part){
672 $types[$part['FAIpartitionType']][$part['FAIpartitionNr']] = 1;
673 }
675 // There can only be four primary partitions per disk - without any logical partition.
676 if(count($types['logical']) == 0){
677 if(count($types['primary']) > 4){
678 $msgs[] = _("You have more than four primary partition table entries in your configuration, please check your configuration twice.");
679 }
680 }else{
681 if(count($types['primary']) > 3){
682 $msgs[] = _("You cannot have more than three primary partition while using logical partitions, please check your configuration twice.");
683 }
684 }
685 }
687 return($msgs);
688 }
691 /* Return plugin informations for acl handling */
692 static function plInfo()
693 {
694 return (array(
695 "plShortName" => _("Partition table entry"),
696 "plDescription" => _("FAI partition table entry"),
697 "plSelfModify" => FALSE,
698 "plDepends" => array(),
699 "plPriority" => 27,
700 "plSection" => array("administration"),
701 "plCategory" => array("fai"),
702 "plProvidedAcls" => array(
703 "DISKcn" => _("Name"),
704 "DISKdescription" => _("Description"),
705 "DISKFAIdiskOption" => _("Disk options"),
706 "FAIpartitionType" => _("Partition type"),
707 "FAIpartitionNr" => _("Partition no."),
708 "FAIfsType" => _("File system type"),
709 "FAImountPoint" => _("Mount point"),
710 "FAIpartitionSize" => _("Partition size"),
711 "FAImountOptions" => _("Mount options"),
712 "FAIfsOptions" => _("File system options"),
713 "FAIpartitionFlags" => _("Partition flags"))
714 ));
715 }
717 }
719 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
720 ?>