Code

Fixed problem with new opsi hosts, config of netbott wasn't written
[gosa.git] / gosa-plugins / opsi / admin / opsi / class_opsigeneric.inc
1 <?php
4 /*! \brief  The opsi client base class.
5             This class can be implemented in tow different ways:
6               * as standalone opsi client
7               * as part of the samba tabs 
8             both types will be detected automatically.
10             This class allows to edit the properties of an opsi client
11              and its products.
12  */
13 class opsiGeneric extends plugin
14 {
15   /* Contains a list of all available netboot products 
16    */
17   private $a_availableNetbootProducts = array();
18   private $a_initial_availableNetbootProducts = array();
19   private $s_selectedNetbootProduct = "";  
20   private $s_initial_selectedNetbootProduct = "";  
22   /* Contains a list of all available local products
23    */
24   private $a_availableLocalProducts = array();
25   private $a_selectedLocalProducts = array();
26   private $a_initial_selectedLocalProducts = array();
28   /* Internal veriables 
29    */ 
30   private $opsi;            // The opsi handle
31   public  $parent = NULL;   // The parent object (in case of samba)
33   private $hostId       = ""; // The host Id of the currently edit opsi host  
34   public  $mac          = ""; // The hosts mac address
35   public  $note         = ""; // A note
36   public  $description  = ""; // The client description
38   public  $initial_mac          = ""; 
39   public  $initial_note         = ""; 
40   public  $initial_description  = ""; 
42   private $init_failed = FALSE; // Is true if the opsi communication failed
43   private $parent_mode = TRUE;  // Is true if this is a standlone plugin. (Not samba)
44   private $is_installed= FALSE; // Is true is the hast is already installed.
46   public $attributes = array("mac","note","description");
49   /*! \brief  Initialize this class 
50       @param  Object    The GOsa base config.
51       @param  String    The Id of the host that we want to edit.
52       @param  Object    The parent object. (in case of samba)
53    */
54   public function __construct($config,$hostId,&$parent = NULL)
55   {
56     /* Create opsi handle
57      */
58     $this->opsi = new opsi($config); 
59     
60     /* Check if we are are part of a windows workstation 
61      */
62     $this->parent = $parent;
63     if($parent instanceof wingeneric){
64       $this->parent_mode = FALSE;
65     }
67     /* Get hostId 
68      */
69     if($hostId != "new"){
70       if(preg_match("/^opsi:/",$hostId)){
71         $this->hostId = preg_replace("/^opsi:=([^,]*),.*$/","\\1",$hostId);
72       }elseif($this->parent instanceof wingeneric){
73         $this->hostId = $this->parent->cn;
74         $this->hostId = preg_replace('/\$$/',"",$this->hostId);
75       }
76     }
77   
78     /* Try to plugin */
79     $this->init();
80   }
81   
83   /*! \brief  Try to load opsi client informations from the 
84                gosa support daemon.
85    */
86   private function init()
87   {
88     $err = FALSE;
89     $this->init_failed = FALSE;
90     $this->initially_was_account = FALSE; 
92     /* Try to load client infos from the gosa support daemon
93      */
94     if(! ($this->hostId == "new" || !empty($this->mac)  )){
95       $list = $this->opsi->list_clients($this->hostId);
96       $err |= $this->opsi->is_error();
98       /* Walk through all returned opsi clients and try to detect 
99           one that matches our hostId.
100          #FIXME Implement an opsi method which returns infos for only one opsi client, not all. 
101        */
102       foreach($list as $entry){
103         if(preg_match("/^".normalizePreg($this->hostId)."$/i",$entry['NAME'][0]['VALUE'])){
104           $this->initially_was_account = TRUE; 
105           foreach(array(
106                 "is_installed" => "LASTSEEN",
107                 "description"  => "DESCRIPTION",
108                 "mac"          => "MAC", 
109                 "note"         => "NOTES") as $des => $src){
110             $des2 = "initial_".$des;
111             $this->$des2 = $this->$des = $entry[$src][0]['VALUE'];
112           } 
113           break;
114         }
115       }
116     }
118     /* Read informations about available netboot products. 
119         If not already done, before.
120      */
121     if(!$err && !count($this->a_availableNetbootProducts)){
122       $this->a_availableNetbootProducts = $this->opsi->get_netboot_products();
123       ksort($this->a_availableNetbootProducts);
124       $err |= $this->opsi->is_error();
125     }
127     /* Read informations about available netboot products. 
128         If not already done, before.
129      */
130     if(!$err && !count($this->a_availableLocalProducts)) {
131       $this->a_availableLocalProducts   = $this->opsi->get_local_products();
132       ksort($this->a_availableLocalProducts);
133       $err |= $this->opsi->is_error();
134     }
136     /* Get products selected by this host.
137      */
138     if(!$err && !empty($this->hostId)) {
139       $tmp = array_keys($this->opsi->get_netboot_products($this->hostId));
140       $err |= $this->opsi->is_error();
141       if(count($tmp) && !$err && !isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
142         $this->s_selectedNetbootProduct = $tmp[0];
143       
144         /* Read configuration for "Netboot Products" */
145         if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct])){
146           $CFG = $this->opsi->get_product_properties($this->s_selectedNetbootProduct,$this->hostId);
147           $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'] = $CFG;
148         }
149       }
150       $err |= $this->opsi->is_error();
151     }
153     /* Get all selected local products 
154      */
155     if(!$err && !empty($this->hostId) && !count($this->a_selectedLocalProducts)) {
156       $tmp = $this->opsi->get_local_products($this->hostId); 
157       $err |= $this->opsi->is_error();
158       $this->a_selectedLocalProducts = $tmp;
159     }
161     /* Load product configuration for all already selected products.
162      */
163     if(!$err && !empty($this->hostId)) {
164       foreach($this->a_selectedLocalProducts as $name => $data){
165         if(!$err && !isset($this->a_selectedLocalProducts[$name]['CFG'])){
166           $CFG = $this->opsi->get_product_properties($name,$this->hostId);
167           $err |= $this->opsi->is_error();
168           $this->a_selectedLocalProducts[$name]['CFG'] = $CFG;
169         }
170       }
171     }
172   
173     /* Check if everything went fine else reset everything and display a retry button 
174      */
175     if($err){
176       $this->init_failed = TRUE;
177       
178     }else{
180       /* Remember initial settings */ 
181       $this->is_account = $this->initially_was_account;
182       $this->a_initial_selectedLocalProducts = $this->a_selectedLocalProducts;
183       $this->s_initial_selectedNetbootProduct = $this->s_selectedNetbootProduct;
184       $this->a_initial_availableNetbootProducts = $this->a_availableNetbootProducts;
186       /* Ensure that a valid netboot is selected product is.
187        */
188       if(empty($this->s_selectedNetbootProduct)){
189         $this->s_selectedNetbootProduct = key($this->a_availableNetbootProducts);
190         echo $this->s_selectedNetbootProduct;
191       }
192     
193     }
194   }
197   /*! \brief  Check given data.
198       @return Array   Returns an array with all issues.
199    */
200   public function check()
201   {
202     return(array());
203     $messages = plugin::check();
205     if(empty($this->hostId)){
206       $messages[] = msgPool::required(_("Name"));
207     }elseif(!preg_match("/\./",$this->hostId)){
209       /* The hostId must contain a domain part 
210        */
211       $messages[] = msgPool::invalid(_("Name"),$this->hostId,"",
212           _("The client name must contain a domain part (e.g. '.company.de')."));
213     }elseif(preg_match("/[^a-z0-9\.\-_]/",$this->hostId)){
214       $messages[] = msgPool::invalid(_("Name"),$this->hostId,"/[a-z0-9\.\-_]/");
215     }
217     /* Ensure that the mac address is valid
218      */
219     if(!tests::is_mac($this->mac) || empty($this->mac)){
220       $messages[] = msgPool::invalid(_("MAC address"),$this->mac,"","00:0C:7F:31:33:F1");
221     }
222     return($messages);
223   }
226   /*! \brief  Create the html ui of this plugin
227       @return String  HTML content.
228    */
229   public function execute()
230   {
231     $display ="";
233     /* The pluign initialization failed due to communication problems with the gosa daemon. 
234        A retry button will be displayed here.
235      */
236     if($this->init_failed){
237       $smarty = get_smarty();
238       $smarty->assign("init_failed",TRUE);
239       $smarty->assign("message",$this->opsi->get_error());
240       return($smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
241     }  
243     /* If we are not a stand alone opsi client, we must be a samba client 
244        which has the opsi tab enabled.
245        Check if the opsi is added or removed and display state buttons.
246      */
247     if(!$this->parent_mode){
248       if(isset($_POST['modify_state'])){
249         if($this->is_account){
250           $this->is_account= FALSE;
251         }elseif(!$this->is_account){
252           $this->is_account= TRUE;
253         }
254       }
255       if($this->is_account){
256         $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("Opsi")), 
257             msgPool::featuresEnabled(_("Opsi")));
258       }else{
259         $display = $this->show_enable_header(msgPool::addFeaturesButton(_("Opsi")), 
260             msgPool::featuresDisabled(_("Opsi")));
261         return($display);
262       } 
263     } 
265     /* Check if we have a sub dialog opened
266      */
267     if(is_object($this->dialog)){
268       $this->dialog->save_object();
269       return($this->dialog->execute());
270     }
272     /* Create HTML output of this plugin
273      */
274     $smarty = get_smarty();
275     foreach($this->attributes as $attr){
276       $smarty->assign($attr,$this->$attr);
277     }
279     $smarty->assign("parent_mode", $this->parent_mode);
280     $smarty->assign("is_installed", $this->is_installed);
281     $smarty->assign("init_failed",FALSE);
282     $divSLP = new divSelectBox();
283     $divALP = new divSelectBox();
285     /* Create list of available local products 
286      */
287     foreach($this->a_availableLocalProducts as $name => $data){
288       if(isset($this->a_selectedLocalProducts[$name])) continue;
290       $add_tab  = array("string"   => "<input type='image' src='images/back.png' name='add_lp_".$name."'>");
291       $name_tab = array("string"   => $name);
292       $desc_tab = array("string"   => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
293           "attach"   => "title='".$data['DESC']."' style='border-right:0px;'");
294       $divALP->AddEntry(array($add_tab,$name_tab,$desc_tab));
295     }
297     /* Create list of selected local products 
298      */
299     ksort($this->a_selectedLocalProducts);
300     foreach($this->a_selectedLocalProducts as $name => $data){
302       $name_tab = array("string"   => $name);
303       $desc_tab = array(
304           "string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
305           "attach" => "title='".$data['DESC']."'");
307       /* Only display edit button, if there is something to edit 
308        */
309       $edit = "<img src='images/empty.png' alt=' '>";
310       if(count($data['CFG'])){
311         $edit = "<input type='image' src='images/lists/edit.png' name='edit_lp_".$name."'>";
312       }
313       $del  = "<input type='image' src='images/lists/trash.png' name='del_lp_".$name."'>";  
315       $opt_tab  = array("string" => $edit.$del,
316           "attach" => "style='border-right:0px; width: 40px; text-align:right;'");
317       $divSLP->AddEntry(array($name_tab,$desc_tab,$opt_tab));
318     }
320     /* Check if netboot product is configurable 
321      */
322     $cfg_able =FALSE;
323     if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
324       $cfg_able = count($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG']);
325     }
327     $smarty->assign("netboot_configurable",$cfg_able);
328     $smarty->assign("hostId", $this->hostId);
329     $smarty->assign("divSLP", $divSLP->DrawList());
330     $smarty->assign("divALP", $divALP->DrawList());
331     $smarty->assign("SNP", $this->s_selectedNetbootProduct);
332     $smarty->assign("ANP", $this->a_availableNetbootProducts);
333     return($display.$smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
334   }
337   /*! \brief  Save modifications using the gosa support daemon.
338    */
339   public function save()
340   {
341     
342     /* Check if we have to create a new opsi client
343         or just have to save client modifications.
344      */
345     if(!$this->initially_was_account && $this->is_account){
346       $res = $this->opsi->add_client($this->hostId,$this->mac,$this->note,$this->description);
347       if($this->opsi->is_error()){
348         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
349         return;
350       }
351     }else{
353       /* Update client modifcations.
354           -Only if necessary  
355        */
356       if($this->note != $this->initial_note || 
357           $this->description != $this->initial_description ||
358           $this->mac != $this->initial_mac){
359         $this->opsi->modify_client($this->hostId,$this->mac,$this->note,$this->description);
360         if($this->opsi->is_error()){
361           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
362           return;
363         }
364       }
365     }
368     /***********
369       Detect local netboot product changes
370        - Check which products were removed.
371        - Check which products were added. 
372      ***********/
375     /* Detect which products were removed an which added.
376      */
377     $del = array_diff_assoc($this->a_initial_selectedLocalProducts,$this->a_selectedLocalProducts);
378     $add = array_diff_assoc($this->a_selectedLocalProducts,$this->a_initial_selectedLocalProducts);
380     /* Remove products from client
381      */
382     foreach($del as $name => $data){
383       $this->opsi->del_product_from_client($name,$this->hostId);
384       if($this->opsi->is_error()){
385         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
386         return;
387       }
388     }
389     
390     /* Add products to client
391        And set the product properties.
392      */
393     foreach($add as $name => $data){
394       $this->opsi->add_product_to_client($name,$this->hostId);
395       if($this->opsi->is_error()){
396         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
397         return;
398       }
399       if(!empty($data['CFG'])){
400         $this->opsi->set_product_properties($name,$data['CFG'],$this->hostId);
401         if($this->opsi->is_error()){
402           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
403           return;
404         }
405       }
406     }
408     /* Save local product properties 
409      */
410     foreach($this->a_selectedLocalProducts as $name => $data){
411       if(isset($del[$name]) || isset($add[$name])) continue;
413       /* Update product properties if there are changes 
414        */
415       $diffs = $this->get_config_changes($data['CFG'],$this->a_initial_selectedLocalProducts[$name]['CFG']);
416       if(count($diffs)){
417         $this->opsi->set_product_properties($name,$diffs,$this->hostId);
418         if($this->opsi->is_error()){
419           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
420           return;
421         }
422       }
423     }
425     /*********
426       Detect Netboot product changes
427        - Check if another netboot product was selected. 
428        - Check if the product properties were changes.
429      *********/
431     /* Update used netboot product. 
432      */
433     if($this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
434       if(!empty($this->s_initial_selectedNetbootProduct)){
435         $this->opsi->del_product_from_client($this->s_initial_selectedNetbootProduct,$this->hostId);
436         if($this->opsi->is_error()){
437           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
438           return;
439         }
440       }
441       $this->opsi->add_product_to_client($this->s_selectedNetbootProduct,$this->hostId);
442       if($this->opsi->is_error()){
443         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
444         return;
445       }
446     }
448     /* Check if we have to update the netboot product properties 
449         This is the case, if this product is newly selected.
450         Or if there was at least one configuration attribute modified.
451      */
452     $cfg_1 = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
453     $cfg_2 = $this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
454     $diffs = $this->get_config_changes($cfg_1,$cfg_2);
455     $to_update = array();
456     if( !$this->initially_was_account || 
457         $this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
458       $to_update = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
459     }elseif(count($diffs)){
460       $to_update = $diffs;
461     }
463     if(count($to_update)){
464       $name = $this->s_selectedNetbootProduct;
465       $this->opsi->set_product_properties($name,$to_update,$this->hostId);
466       if($this->opsi->is_error()){
467         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
468         return;
469       }
470     }
471   }
473   
474   public function get_config_changes($c1,$c2)
475   {
476     /* Get key which are not present in both entries 
477      */
478     $res = array();
479     foreach($c2 as $name => $value){
480       if(!isset($c1[$name]) || $c1[$name]['DEFAULT'] != $c2[$name]['DEFAULT']){
481         $res[$name] = $c2[$name];
482       }
483     }
484     foreach($c1 as $name => $value){
485       if(!isset($c2[$name]) || $c2[$name]['DEFAULT'] != $c1[$name]['DEFAULT']){
486         $res[$name] = $c1[$name];
487       }
488     }
489     return($res);
490   }
493   /*! \brief  Removes the opsi client 
494    */  
495   public function remove_from_parent()
496   {
497     $this->opsi->del_client($this->hostId);
498     if($this->opsi->is_error()){
499       msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
500       return;
501     }
502   }
505   /*! \brief  Save html posts 
506    */
507   public function save_object()
508   {
509     /* Init failed; reinit is triggered here.
510      */
511     if(isset($_POST['reinit']) && $this->init_failed){
512       $this->init();
513     }
515     /* Property are currently edited, close the dialog. 
516      */
517     if(isset($_POST['cancel_properties']) && is_object($this->dialog)){
518       $this->dialog = NULL;
519     }
520   
521     /* Save product property changes 
522      */
523     if(isset($_POST['save_properties']) && ($this->dialog instanceof opsiProperties)){
524       $this->dialog->save_object();
525       $pro = $this->dialog->get_product();
526       $CFG = $this->dialog->get_cfg();
527       if(isset($this->a_selectedLocalProducts[$pro])){
528         $this->a_selectedLocalProducts[$pro]['CFG'] = $CFG;
529         $this->dialog = NULL;
530       }elseif($this->s_selectedNetbootProduct == $pro){
531         $this->a_availableNetbootProducts[$pro]['CFG'] = $CFG;
532         $this->dialog = NULL;
533       }else{
534         trigger_error("Fatal, unknown product was configured.");
535       }
536     }
538     /* Save html post
539      */
540     if(isset($_POST['opsiGeneric_posted'])){
542       plugin::save_object();
544       /* Get hostId 
545        */
546       if(isset($_POST['hostId']) && $this->parent_mode){
547         $this->hostId = get_post('hostId');
548       }
550       /* Send actions like 'install' or 'wake' to the si server 
551        */
552       if(isset($_POST['opsi_action']) && isset($_POST['opsi_trigger_action']) && $this->parent_mode){
553         $action = $_POST['opsi_action'];
554         if($action == "install"){
555           $this->install_client(); 
556         }
557       }
559       /* Get selected netboot product.
560        */
561       if(isset($_POST['opsi_netboot_product'])){
562         $SNP = trim($_POST['opsi_netboot_product']);
563         if(isset($this->a_availableNetbootProducts[$SNP])){
565           if(!isset($this->a_availableNetbootProducts[$SNP]['CFG'])){
566             $CFG = $this->opsi->get_product_properties($SNP);
567             $this->a_availableNetbootProducts[$SNP]['CFG'] = $CFG;
568             if($this->opsi->is_error()){
569               $this->init_failed = TRUE;
570               return;
571             }
572           }
573           $this->s_selectedNetbootProduct = $SNP;
574         }
575       }
577       /* Add/remove/edit local products 
578        */
579       foreach($_POST as $name => $value){
581         /* Check if netboot product configuration is requested 
582          */
583         if(preg_match("/^configure_netboot/",$name)){
584           $pro = $this->s_selectedNetbootProduct;
585           $cfg = $this->a_availableNetbootProducts[$pro]['CFG'];
586           $this->dialog = new opsiProperties($this->config,$pro,$cfg,$this->hostId);
587           break;
588         }
589       
590         /* Add product 
591          */
592         if(preg_match("/^add_lp_/",$name)){
593           $product = preg_replace("/^add_lp_(.*)_.$/","\\1",$name);
594           if(isset($this->a_availableLocalProducts[$product]) && !isset($this->a_selectedLocalProducts[$product])){
595             $this->a_selectedLocalProducts[$product] = $this->a_availableLocalProducts[$product];
596             $CFG = $this->opsi->get_product_properties($product);
597             if($this->opsi->is_error()){
598               $this->init_failed = TRUE;
599               return;
600             }
601             $this->a_selectedLocalProducts[$product]['CFG'] = $CFG;
602           }
603           break;
604         }
605   
606         /* Delete product 
607          */
608         if(preg_match("/^del_lp_/",$name)){
609           $product = preg_replace("/^del_lp_(.*)_.$/","\\1",$name);
610           if(isset($this->a_selectedLocalProducts[$product])){
611             unset($this->a_selectedLocalProducts[$product]);
612           }
613           break;
614         }
615       
616         /* Edit a product  
617          */
618         if(preg_match("/^edit_lp_/",$name)){
619           $product = preg_replace("/^edit_lp_(.*)_.$/","\\1",$name);
620           $this->dialog = new opsiProperties($this->config,
621               $product,$this->a_selectedLocalProducts[$product]['CFG'],$this->hostId);
622           break;
623         }
624       }   
625     }
626   }
629   /* Triggers client installation 
630    */
631   function install_client()
632   {
633     $this->opsi->send_action("install",$this->hostId,$this->mac);
634     if($this->opsi->is_error()){
635       msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
636     }
637   }
640   /* Return plugin informations for acl handling */
641   static function plInfo()
642   {
643     return (array(
644           "plShortName"   => _("Generic"),
645           "plDescription" => _("Opsi generic"),
646           "plSelfModify"  => FALSE,
647           "plDepends"     => array(),
648           "plPriority"    => 1,
649           "plSection"     => array("administration"),
650           "plCategory"    => array("opsi" => array("description"  => _("Opsi client"),
651                                                      "objectClass"  => "dummy_class_opsi")),
652           "plProvidedAcls"=> array()
653           ));
654   }
659 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
660 ?>