Code

Updated opsi
[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 $standalone  = 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->standalone  = 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     /* We are a standalone plugin.
93      */
94     if($this->standalone ) {
95       $this->is_account = TRUE;
96     }
98     /* Try to load client infos from the gosa support daemon
99      */
100     if(!empty($this->hostId)){
102       $list = $this->opsi->list_clients($this->hostId);
103       $err |= $this->opsi->is_error();
105       /* Walk through all returned opsi clients and try to detect 
106           one that matches our hostId.
107          #FIXME Implement an opsi method which returns infos for only one opsi client, not all. 
108        */
109       foreach($list as $entry){
110         if(preg_match("/^".normalizePreg($this->hostId)."$/i",$entry['NAME'][0]['VALUE'])){
111           $this->initially_was_account = TRUE; 
112           $this->is_account = TRUE;
113           foreach(array(
114                 "is_installed" => "LASTSEEN",
115                 "description"  => "DESCRIPTION",
116                 "mac"          => "MAC", 
117                 "note"         => "NOTES") as $des => $src){
118             $des2 = "initial_".$des;
119             $this->$des2 = $this->$des = $entry[$src][0]['VALUE'];
120           } 
121           break;
122         }
123       }
124     }
126     /* Read informations about available netboot products. 
127         If not already done, before.
128      */
129     if(!$err && !count($this->a_availableNetbootProducts)){
130       $this->a_availableNetbootProducts = $this->opsi->get_netboot_products();
131       ksort($this->a_availableNetbootProducts);
132       $err |= $this->opsi->is_error();
133     }
135     /* Read informations about available netboot products. 
136         If not already done, before.
137      */
138     if(!$err && !count($this->a_availableLocalProducts)) {
139       $this->a_availableLocalProducts   = $this->opsi->get_local_products();
140       ksort($this->a_availableLocalProducts);
141       $err |= $this->opsi->is_error();
142     }
144     /* Get products selected by this host.
145      */
146     if(!$err && !empty($this->hostId)) {
147       $tmp = array_keys($this->opsi->get_netboot_products($this->hostId));
148       $err |= $this->opsi->is_error();
149       if(count($tmp) && !$err && !isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
150         $this->s_selectedNetbootProduct = $tmp[0];
151       
152         /* Read configuration for "Netboot Products" */
153         if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct])){
154           $CFG = $this->opsi->get_product_properties($this->s_selectedNetbootProduct,$this->hostId);
155           $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'] = $CFG;
156         }
157       }
158       $err |= $this->opsi->is_error();
159     }
161     /* Get all selected local products 
162      */
163     if(!$err && !empty($this->hostId) && !count($this->a_selectedLocalProducts)) {
164       $tmp = $this->opsi->get_local_products($this->hostId); 
165       $err |= $this->opsi->is_error();
166       $this->a_selectedLocalProducts = $tmp;
167     }
169     /* Load product configuration for all already selected products.
170      */
171     if(!$err && !empty($this->hostId)) {
172       foreach($this->a_selectedLocalProducts as $name => $data){
173         if(!$err && !isset($this->a_selectedLocalProducts[$name]['CFG'])){
174           $CFG = $this->opsi->get_product_properties($name,$this->hostId);
175           $err |= $this->opsi->is_error();
176           $this->a_selectedLocalProducts[$name]['CFG'] = $CFG;
177         }
178       }
179     }
180   
181     /* Check if everything went fine else reset everything and display a retry button 
182      */
183     if($err){
184       $this->init_failed = TRUE;
185       
186     }else{
188       /* Remember initial settings */
189       $this->a_initial_selectedLocalProducts = $this->a_selectedLocalProducts;
190       $this->s_initial_selectedNetbootProduct = $this->s_selectedNetbootProduct;
191       $this->a_initial_availableNetbootProducts = $this->a_availableNetbootProducts;
193       /* Ensure that a valid netboot is selected product is.
194        */
195       if(empty($this->s_selectedNetbootProduct)){
196         $this->s_selectedNetbootProduct = key($this->a_availableNetbootProducts);
197       }
198     }
199   }
202   /*! \brief  Check given data.
203       @return Array   Returns an array with all issues.
204    */
205   public function check()
206   {
207     return(array());
208     $messages = plugin::check();
210     if(empty($this->hostId)){
211       $messages[] = msgPool::required(_("Name"));
212     }elseif(!preg_match("/\./",$this->hostId)){
214       /* The hostId must contain a domain part 
215        */
216       $messages[] = msgPool::invalid(_("Name"),$this->hostId,"",
217           _("The field 'Name' must contain a domain part!"));
218     }elseif(preg_match("/[^a-z0-9\.\-_]/",$this->hostId)){
219       $messages[] = msgPool::invalid(_("Name"),$this->hostId,"/[a-z0-9\.\-_]/");
220     }
222     /* Ensure that the mac address is valid
223      */
224     if(!tests::is_mac($this->mac) || empty($this->mac)){
225       $messages[] = msgPool::invalid(_("MAC address"),$this->mac,"","00:0C:7F:31:33:F1");
226     }
227     return($messages);
228   }
231   /*! \brief  Create the html ui of this plugin
232       @return String  HTML content.
233    */
234   public function execute()
235   {
236     $display ="";
238     /* The pluign initialization failed due to communication problems with the gosa daemon. 
239        A retry button will be displayed here.
240      */
241     if($this->init_failed){
242       $smarty = get_smarty();
243       $smarty->assign("standalone ", $this->standalone );
244       $smarty->assign("init_failed",TRUE);
245       $smarty->assign("message",$this->opsi->get_error());
246       return($smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
247     }  
249     /* If we are not a stand alone opsi client, we must be a samba client 
250        which has the opsi tab enabled.
251        Check if the opsi is added or removed and display state buttons.
252      */
253     if(!$this->standalone ){
254       if(isset($_POST['modify_state'])){
255         if($this->is_account){
256           $this->is_account= FALSE;
257         }elseif(!$this->is_account){
258           $this->is_account= TRUE;
259         }
260       }
261       if($this->is_account){
262         $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("OPSI")), 
263             msgPool::featuresEnabled(_("OPSI")));
264       }else{
265         $display = $this->show_enable_header(msgPool::addFeaturesButton(_("OPSI")), 
266             msgPool::featuresDisabled(_("OPSI")));
267         return($display);
268       } 
269     } 
271     /* Check if we have a sub dialog opened
272      */
273     if(is_object($this->dialog)){
274       $this->dialog->save_object();
275       return($this->dialog->execute());
276     }
278     /* Create HTML output of this plugin
279      */
280     $smarty = get_smarty();
281     $smarty->assign("standalone", $this->standalone );
282     foreach($this->attributes as $attr){
283       $smarty->assign($attr,$this->$attr);
284     }
285     $smarty->assign("is_installed", $this->is_installed);
286     $smarty->assign("init_failed",FALSE);
287     $divSLP = new divSelectBox();
288     $divALP = new divSelectBox();
290     /* Create list of available local products 
291      */
292     foreach($this->a_availableLocalProducts as $name => $data){
293       if(isset($this->a_selectedLocalProducts[$name])) continue;
295       $add_tab  = array("string"   => "<input type='image' src='images/back.png' name='add_lp_".$name."'>");
296       $name_tab = array("string"   => $name);
297       $desc_tab = array("string"   => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
298           "attach"   => "title='".$data['DESC']."' style='border-right:0px;'");
299       $divALP->AddEntry(array($add_tab,$name_tab,$desc_tab));
300     }
302     /* Create list of selected local products 
303      */
304     ksort($this->a_selectedLocalProducts);
305     foreach($this->a_selectedLocalProducts as $name => $data){
307       $name_tab = array("string"   => $name);
308       $desc_tab = array(
309           "string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
310           "attach" => "title='".$data['DESC']."'");
312       /* Only display edit button, if there is something to edit 
313        */
314       $edit = "<img src='images/empty.png' alt=' '>";
315       if(count($data['CFG'])){
316         $edit = "<input type='image' src='images/lists/edit.png' name='edit_lp_".$name."'>";
317       }
318       $del  = "<input type='image' src='images/lists/trash.png' name='del_lp_".$name."'>";  
320       $opt_tab  = array("string" => $edit.$del,
321           "attach" => "style='border-right:0px; width: 40px; text-align:right;'");
322       $divSLP->AddEntry(array($name_tab,$desc_tab,$opt_tab));
323     }
325     /* Check if netboot product is configurable 
326      */
327     $cfg_able =FALSE;
328     if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
329       $cfg_able = count($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG']);
330     }
332     $smarty->assign("netboot_configurable",$cfg_able);
333     $smarty->assign("hostId", $this->hostId);
334     $smarty->assign("divSLP", $divSLP->DrawList());
335     $smarty->assign("divALP", $divALP->DrawList());
336     $smarty->assign("SNP", $this->s_selectedNetbootProduct);
337     $smarty->assign("ANP", $this->a_availableNetbootProducts);
338     return($display.$smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
339   }
342   /*! \brief  Save modifications using the gosa support daemon.
343    */
344   public function save()
345   {
346     
347     /* Check if we have to create a new opsi client
348         or just have to save client modifications.
349      */
350     if(!$this->initially_was_account && $this->is_account){
351       $res = $this->opsi->add_client($this->hostId,$this->mac,$this->note,$this->description);
352       if($this->opsi->is_error()){
353         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
354         return;
355       }
356     }else{
358       /* Update client modifcations.
359           -Only if necessary  
360        */
361       if($this->note != $this->initial_note || 
362           $this->description != $this->initial_description ||
363           $this->mac != $this->initial_mac){
364         $this->opsi->modify_client($this->hostId,$this->mac,$this->note,$this->description);
365         if($this->opsi->is_error()){
366           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
367           return;
368         }
369       }
370     }
373     /***********
374       Detect local netboot product changes
375        - Check which products were removed.
376        - Check which products were added. 
377      ***********/
380     /* Detect which products were removed an which added.
381      */
382     $del = array_diff_assoc($this->a_initial_selectedLocalProducts,$this->a_selectedLocalProducts);
383     $add = array_diff_assoc($this->a_selectedLocalProducts,$this->a_initial_selectedLocalProducts);
385     /* Remove products from client
386      */
387     foreach($del as $name => $data){
388       $this->opsi->del_product_from_client($name,$this->hostId);
389       if($this->opsi->is_error()){
390         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
391         return;
392       }
393     }
394     
395     /* Add products to client
396        And set the product properties.
397      */
398     foreach($add as $name => $data){
399       $this->opsi->add_product_to_client($name,$this->hostId);
400       if($this->opsi->is_error()){
401         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
402         return;
403       }
404       if(!empty($data['CFG'])){
405         $this->opsi->set_product_properties($name,$data['CFG'],$this->hostId);
406         if($this->opsi->is_error()){
407           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
408           return;
409         }
410       }
411     }
413     /* Save local product properties 
414      */
415     foreach($this->a_selectedLocalProducts as $name => $data){
416       if(isset($del[$name]) || isset($add[$name])) continue;
418       /* Update product properties if there are changes 
419        */
420       $diffs = $this->get_config_changes($data['CFG'],$this->a_initial_selectedLocalProducts[$name]['CFG']);
421       if(count($diffs)){
422         $this->opsi->set_product_properties($name,$diffs,$this->hostId);
423         if($this->opsi->is_error()){
424           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
425           return;
426         }
427       }
428     }
430     /*********
431       Detect Netboot product changes
432        - Check if another netboot product was selected. 
433        - Check if the product properties were changes.
434      *********/
436     /* Update used netboot product. 
437      */
438     if($this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
439       if(!empty($this->s_initial_selectedNetbootProduct)){
440         $this->opsi->del_product_from_client($this->s_initial_selectedNetbootProduct,$this->hostId);
441         if($this->opsi->is_error()){
442           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
443           return;
444         }
445       }
446       $this->opsi->add_product_to_client($this->s_selectedNetbootProduct,$this->hostId);
447       if($this->opsi->is_error()){
448         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
449         return;
450       }
451     }
453     /* Check if we have to update the netboot product properties 
454         This is the case, if this product is newly selected.
455         Or if there was at least one configuration attribute modified.
456      */
457     $cfg_1 = $cfg_2 = array();
458     if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
459       $cfg_1 = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
460     }
461     if(isset($this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
462       $cfg_2 = $this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
463     }
464     $diffs = $this->get_config_changes($cfg_1,$cfg_2);
465     $to_update = array();
466     if( !$this->initially_was_account || 
467         $this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
468       $to_update = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
469     }elseif(count($diffs)){
470       $to_update = $diffs;
471     }
473     if(count($to_update)){
474       $name = $this->s_selectedNetbootProduct;
475       $this->opsi->set_product_properties($name,$to_update,$this->hostId);
476       if($this->opsi->is_error()){
477         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
478         return;
479       }
480     }
481   }
483   
484   public function get_config_changes($c1,$c2)
485   {
486     /* Get key which are not present in both entries 
487      */
488     $res = array();
489     foreach($c2 as $name => $value){
490       if(!isset($c1[$name]) || $c1[$name]['DEFAULT'] != $c2[$name]['DEFAULT']){
491         $res[$name] = $c2[$name];
492       }
493     }
494     foreach($c1 as $name => $value){
495       if(!isset($c2[$name]) || $c2[$name]['DEFAULT'] != $c1[$name]['DEFAULT']){
496         $res[$name] = $c1[$name];
497       }
498     }
499     return($res);
500   }
503   /*! \brief  Removes the opsi client 
504    */  
505   public function remove_from_parent()
506   {
507     $this->opsi->del_client($this->hostId);
508     if($this->opsi->is_error()){
509       msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
510       return;
511     }
512   }
515   /*! \brief  Save html posts 
516    */
517   public function save_object()
518   {
519     /* Init failed; reinit is triggered here.
520      */
521     if(isset($_POST['reinit']) && $this->init_failed){
522       $this->init();
523     }
525     /* Property are currently edited, close the dialog. 
526      */
527     if(isset($_POST['cancel_properties']) && is_object($this->dialog)){
528       $this->dialog = NULL;
529     }
530   
531     /* Save product property changes 
532      */
533     if(isset($_POST['save_properties']) && ($this->dialog instanceof opsiProperties)){
534       $this->dialog->save_object();
535       $pro = $this->dialog->get_product();
536       $CFG = $this->dialog->get_cfg();
537       if(isset($this->a_selectedLocalProducts[$pro])){
538         $this->a_selectedLocalProducts[$pro]['CFG'] = $CFG;
539         $this->dialog = NULL;
540       }elseif($this->s_selectedNetbootProduct == $pro){
541         $this->a_availableNetbootProducts[$pro]['CFG'] = $CFG;
542         $this->dialog = NULL;
543       }else{
544         trigger_error("Fatal, unknown product was configured.");
545       }
546     }
548     /* Save html post
549      */
550     if(isset($_POST['opsiGeneric_posted'])){
552       plugin::save_object();
554       /* Get hostId 
555        */
556       if(isset($_POST['hostId']) && $this->standalone ){
557         $this->hostId = get_post('hostId');
558       }
560       /* Send actions like 'install' or 'wake' to the si server 
561        */
562       if(isset($_POST['opsi_action']) && isset($_POST['opsi_trigger_action']) && $this->standalone ){
563         $action = $_POST['opsi_action'];
564         if($action == "install"){
565           $this->install_client(); 
566         }
567       }
569       /* Get selected netboot product.
570        */
571       if(isset($_POST['opsi_netboot_product'])){
572         $SNP = trim($_POST['opsi_netboot_product']);
573         if(isset($this->a_availableNetbootProducts[$SNP])){
575           if(!isset($this->a_availableNetbootProducts[$SNP]['CFG'])){
576             $CFG = $this->opsi->get_product_properties($SNP);
577             $this->a_availableNetbootProducts[$SNP]['CFG'] = $CFG;
578             if($this->opsi->is_error()){
579               $this->init_failed = TRUE;
580               return;
581             }
582           }
583           $this->s_selectedNetbootProduct = $SNP;
584         }
585       }
587       /* Add/remove/edit local products 
588        */
589       foreach($_POST as $name => $value){
591         /* Check if netboot product configuration is requested 
592          */
593         if(preg_match("/^configure_netboot/",$name)){
594           $pro = $this->s_selectedNetbootProduct;
595           $cfg = $this->a_availableNetbootProducts[$pro]['CFG'];
596           $this->dialog = new opsiProperties($this->config,$pro,$cfg,$this->hostId);
597           break;
598         }
599       
600         /* Add product 
601          */
602         if(preg_match("/^add_lp_/",$name)){
603           $product = preg_replace("/^add_lp_(.*)_.$/","\\1",$name);
604           if(isset($this->a_availableLocalProducts[$product]) && !isset($this->a_selectedLocalProducts[$product])){
605             $this->a_selectedLocalProducts[$product] = $this->a_availableLocalProducts[$product];
606             $CFG = $this->opsi->get_product_properties($product);
607             if($this->opsi->is_error()){
608               $this->init_failed = TRUE;
609               return;
610             }
611             $this->a_selectedLocalProducts[$product]['CFG'] = $CFG;
612           }
613           break;
614         }
615   
616         /* Delete product 
617          */
618         if(preg_match("/^del_lp_/",$name)){
619           $product = preg_replace("/^del_lp_(.*)_.$/","\\1",$name);
620           if(isset($this->a_selectedLocalProducts[$product])){
621             unset($this->a_selectedLocalProducts[$product]);
622           }
623           break;
624         }
625       
626         /* Edit a product  
627          */
628         if(preg_match("/^edit_lp_/",$name)){
629           $product = preg_replace("/^edit_lp_(.*)_.$/","\\1",$name);
630           $this->dialog = new opsiProperties($this->config,
631               $product,$this->a_selectedLocalProducts[$product]['CFG'],$this->hostId);
632           break;
633         }
634       }   
635     }
636   }
639   /* Triggers client installation 
640    */
641   function install_client()
642   {
643     $this->opsi->send_action("install",$this->hostId,$this->mac);
644     if($this->opsi->is_error()){
645       msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
646     }
647   }
650   /* Return plugin informations for acl handling */
651   static function plInfo()
652   {
653     return (array(
654           "plShortName"   => _("Generic"),
655           "plDescription" => _("OPSI generic"),
656           "plSelfModify"  => FALSE,
657           "plDepends"     => array(),
658           "plPriority"    => 1,
659           "plSection"     => array("administration"),
660           "plCategory"    => array("opsi" => array("description"  => _("OPSI client"),
661                                                      "objectClass"  => "dummy_class_opsi")),
662           "plProvidedAcls"=> array()
663           ));
664   }
669 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
670 ?>