Code

plugin opsi_com gets more wrapper functions for license management
[gosa.git] / gosa-si / server / events / opsi_com.pm
1 ## @file
2 # @details A GOsa-SI-server event module containing all functions for message handling.
3 # @brief Implementation of an event module for GOsa-SI-server. 
6 package opsi_com;
7 use Exporter;
8 @ISA = qw(Exporter);
9 my @events = (
10     "get_events",
11     "opsi_install_client",
12     "opsi_get_netboot_products",  
13     "opsi_get_local_products",
14     "opsi_get_client_hardware",
15     "opsi_get_client_software",
16     "opsi_get_product_properties",
17     "opsi_set_product_properties",
18     "opsi_list_clients",
19     "opsi_del_client",
20     "opsi_add_client",
21     "opsi_modify_client",
22     "opsi_add_product_to_client",
23     "opsi_del_product_from_client",
24         "opsi_createLicensePool",
25         "opsi_getLicensePoolIds_list",
26         "opsi_getLicensePool_hash",
27         "opsi_deleteLicensePool",
28         "opsi_createLicense",
29    );
30 @EXPORT = @events;
32 use strict;
33 use warnings;
34 use GOSA::GosaSupportDaemon;
35 use Data::Dumper;
36 use XML::Quote qw(:all);
39 BEGIN {}
41 END {}
43 ## @method get_events()
44 # A brief function returning a list of functions which are exported by importing the module.
45 # @return List of all provided functions
46 sub get_events {
47     return \@events;
48 }
50 ## @method opsi_add_product_to_client
51 # Adds an Opsi product to an Opsi client.
52 # @param msg - STRING - xml message with tags hostId and productId
53 # @param msg_hash - HASHREF - message information parsed into a hash
54 # @param session_id - INTEGER - POE session id of the processing of this message
55 # @return out_msg - STRING - feedback to GOsa in success and error case
56 sub opsi_add_product_to_client {
57     my ($msg, $msg_hash, $session_id) = @_;
58     my $header = @{$msg_hash->{'header'}}[0];
59     my $source = @{$msg_hash->{'source'}}[0];
60     my $target = @{$msg_hash->{'target'}}[0];
61     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
62     my ($hostId, $productId);
63     my $error = 0;
65     # Build return message
66     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
67     if (defined $forward_to_gosa) {
68         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
69     }
71     # Sanity check of needed parameter
72     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
73         $error++;
74         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
75         &add_content2xml_hash($out_hash, "error", "hostId");
76         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
78     }
79     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
80         $error++;
81         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
82         &add_content2xml_hash($out_hash, "error", "productId");
83         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
84     }
86     if (not $error) {
87         # Get hostId
88         $hostId = @{$msg_hash->{'hostId'}}[0];
89         &add_content2xml_hash($out_hash, "hostId", $hostId);
91         # Get productID
92         $productId = @{$msg_hash->{'productId'}}[0];
93         &add_content2xml_hash($out_hash, "productId", $productId);
95         # Do an action request for all these -> "setup".
96         my $callobj = {
97             method  => 'setProductActionRequest',
98             params  => [ $productId, $hostId, "setup" ],
99             id  => 1, }; 
101         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
102         my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
103         if ($sres_err){
104             &main::daemon_log("$session_id ERROR: cannot add product: ".$sres_err_string, 1);
105             &add_content2xml_hash($out_hash, "error", $sres_err_string);
106         }
107     } 
109     # return message
110     return ( &create_xml_string($out_hash) );
113 ## @method opsi_del_product_from_client
114 # Deletes an Opsi-product from an Opsi-client. 
115 # @param msg - STRING - xml message with tags hostId and productId
116 # @param msg_hash - HASHREF - message information parsed into a hash
117 # @param session_id - INTEGER - POE session id of the processing of this message
118 # @return out_msg - STRING - feedback to GOsa in success and error case
119 sub opsi_del_product_from_client {
120     my ($msg, $msg_hash, $session_id) = @_;
121     my $header = @{$msg_hash->{'header'}}[0];
122     my $source = @{$msg_hash->{'source'}}[0];
123     my $target = @{$msg_hash->{'target'}}[0];
124     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
125     my ($hostId, $productId);
126     my $error = 0;
127     my ($sres, $sres_err, $sres_err_string);
129     # Build return message
130     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
131     if (defined $forward_to_gosa) {
132         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
133     }
135     # Sanity check of needed parameter
136     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
137         $error++;
138         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
139         &add_content2xml_hash($out_hash, "error", "hostId");
140         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
142     }
143     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
144         $error++;
145         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
146         &add_content2xml_hash($out_hash, "error", "productId");
147         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
148     }
150     # All parameter available
151     if (not $error) {
152         # Get hostId
153         $hostId = @{$msg_hash->{'hostId'}}[0];
154         &add_content2xml_hash($out_hash, "hostId", $hostId);
156         # Get productID
157         $productId = @{$msg_hash->{'productId'}}[0];
158         &add_content2xml_hash($out_hash, "productId", $productId);
161 #TODO : check the results for more than one entry which is currently installed
162         #$callobj = {
163         #    method  => 'getProductDependencies_listOfHashes',
164         #    params  => [ $productId ],
165         #    id  => 1, };
166         #
167         #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
168         #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
169         #if ($sres_err){
170         #  &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
171         #  &add_content2xml_hash($out_hash, "error", $sres_err_string);
172         #  return ( &create_xml_string($out_hash) );
173         #}
176         # Check to get product action list 
177         my $callobj = {
178             method  => 'getPossibleProductActions_list',
179             params  => [ $productId ],
180             id  => 1, };
181         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
182         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
183         if ($sres_err){
184             &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
185             &add_content2xml_hash($out_hash, "error", $sres_err_string);
186             $error++;
187         }
188     }
190     # Check action uninstall of product
191     if (not $error) {
192         my $uninst_possible= 0;
193         foreach my $r (@{$sres->result}) {
194             if ($r eq 'uninstall') {
195                 $uninst_possible= 1;
196             }
197         }
198         if (!$uninst_possible){
199             &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
200             &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
201             $error++;
202         }
203     }
205     # Set product state to "none"
206     # Do an action request for all these -> "setup".
207     if (not $error) {
208         my $callobj = {
209             method  => 'setProductActionRequest',
210             params  => [ $productId, $hostId, "none" ],
211             id  => 1, 
212         }; 
213         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
214         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
215         if ($sres_err){
216             &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
217             &add_content2xml_hash($out_hash, "error", $sres_err_string);
218         }
219     }
221     # Return message
222     return ( &create_xml_string($out_hash) );
225 ## @method opsi_add_client
226 # Adds an Opsi client to Opsi.
227 # @param msg - STRING - xml message with tags hostId and macaddress
228 # @param msg_hash - HASHREF - message information parsed into a hash
229 # @param session_id - INTEGER - POE session id of the processing of this message
230 # @return out_msg - STRING - feedback to GOsa in success and error case
231 sub opsi_add_client {
232     my ($msg, $msg_hash, $session_id) = @_;
233     my $header = @{$msg_hash->{'header'}}[0];
234     my $source = @{$msg_hash->{'source'}}[0];
235     my $target = @{$msg_hash->{'target'}}[0];
236     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
237     my ($hostId, $mac);
238     my $error = 0;
239     my ($sres, $sres_err, $sres_err_string);
241     # Build return message with twisted target and source
242     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
243     if (defined $forward_to_gosa) {
244         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
245     }
247     # Sanity check of needed parameter
248     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
249         $error++;
250         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
251         &add_content2xml_hash($out_hash, "error", "hostId");
252         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
253     }
254     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
255         $error++;
256         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
257         &add_content2xml_hash($out_hash, "error", "macaddress");
258         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
259     }
261     if (not $error) {
262         # Get hostId
263         $hostId = @{$msg_hash->{'hostId'}}[0];
264         &add_content2xml_hash($out_hash, "hostId", $hostId);
266         # Get macaddress
267         $mac = @{$msg_hash->{'macaddress'}}[0];
268         &add_content2xml_hash($out_hash, "macaddress", $mac);
270         my $name= $hostId;
271         $name=~ s/^([^.]+).*$/$1/;
272         my $domain= $hostId;
273         $domain=~ s/^[^.]+\.(.*)$/$1/;
274         my ($description, $notes, $ip);
276         if (defined @{$msg_hash->{'description'}}[0]){
277             $description = @{$msg_hash->{'description'}}[0];
278         }
279         if (defined @{$msg_hash->{'notes'}}[0]){
280             $notes = @{$msg_hash->{'notes'}}[0];
281         }
282         if (defined @{$msg_hash->{'ip'}}[0]){
283             $ip = @{$msg_hash->{'ip'}}[0];
284         }
286         my $callobj;
287         $callobj = {
288             method  => 'createClient',
289             params  => [ $name, $domain, $description, $notes, $ip, $mac ],
290             id  => 1,
291         };
293         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
294         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
295         if ($sres_err){
296             &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
297             &add_content2xml_hash($out_hash, "error", $sres_err_string);
298         } else {
299             &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5); 
300         }
301     }
303     # Return message
304     return ( &create_xml_string($out_hash) );
307 ## @method opsi_modify_client
308 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
309 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
310 # @param msg_hash - HASHREF - message information parsed into a hash
311 # @param session_id - INTEGER - POE session id of the processing of this message    
312 # @return out_msg - STRING - feedback to GOsa in success and error case
313 sub opsi_modify_client {
314     my ($msg, $msg_hash, $session_id) = @_;
315     my $header = @{$msg_hash->{'header'}}[0];
316     my $source = @{$msg_hash->{'source'}}[0];
317     my $target = @{$msg_hash->{'target'}}[0];
318     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
319     my $hostId;
320     my $error = 0;
321     my ($sres, $sres_err, $sres_err_string);
323     # Build return message with twisted target and source
324     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
325     if (defined $forward_to_gosa) {
326         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
327     }
329     # Sanity check of needed parameter
330     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
331         $error++;
332         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
333         &add_content2xml_hash($out_hash, "error", "hostId");
334         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
335     }
337     if (not $error) {
338         # Get hostId
339         $hostId = @{$msg_hash->{'hostId'}}[0];
340         &add_content2xml_hash($out_hash, "hostId", $hostId);
341         my $name= $hostId;
342         $name=~ s/^([^.]+).*$/$1/;
343         my $domain= $hostId;
344         $domain=~ s/^[^.]+(.*)$/$1/;
346         # Modify description, notes or mac if defined
347         my ($description, $notes, $mac);
348         my $callobj;
349         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
350             $description = @{$msg_hash->{'description'}}[0];
351             $callobj = {
352                 method  => 'setHostDescription',
353                 params  => [ $hostId, $description ],
354                 id  => 1,
355             };
356             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
357             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
358             if ($sres_err){
359                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
360                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
361             }
362         }
363         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
364             $notes = @{$msg_hash->{'notes'}}[0];
365             $callobj = {
366                 method  => 'setHostNotes',
367                 params  => [ $hostId, $notes ],
368                 id  => 1,
369             };
370             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
371             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
372             if ($sres_err){
373                 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
374                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
375             }
376         }
377         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
378             $mac = @{$msg_hash->{'mac'}}[0];
379             $callobj = {
380                 method  => 'setMacAddress',
381                 params  => [ $hostId, $mac ],
382                 id  => 1,
383             };
384             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
385             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
386             if ($sres_err){
387                 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
388                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
389             }
390         }
391     }
393     # Return message
394     return ( &create_xml_string($out_hash) );
397     
398 ## @method opsi_get_netboot_products
399 # Get netboot products for specific host.
400 # @param msg - STRING - xml message with tag hostId
401 # @param msg_hash - HASHREF - message information parsed into a hash
402 # @param session_id - INTEGER - POE session id of the processing of this message
403 # @return out_msg - STRING - feedback to GOsa in success and error case
404 sub opsi_get_netboot_products {
405     my ($msg, $msg_hash, $session_id) = @_;
406     my $header = @{$msg_hash->{'header'}}[0];
407     my $source = @{$msg_hash->{'source'}}[0];
408     my $target = @{$msg_hash->{'target'}}[0];
409     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
410     my $hostId;
411     my $xml_msg;
413     # Build return message with twisted target and source
414     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
415     if (defined $forward_to_gosa) {
416         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
417     }
419     # Get hostId if defined
420     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
421         $hostId = @{$msg_hash->{'hostId'}}[0];
422         &add_content2xml_hash($out_hash, "hostId", $hostId);
423     }
425     &add_content2xml_hash($out_hash, "xxx", "");
426     $xml_msg = &create_xml_string($out_hash);
427     # For hosts, only return the products that are or get installed
428     my $callobj;
429     $callobj = {
430         method  => 'getNetBootProductIds_list',
431         params  => [ ],
432         id  => 1,
433     };
434     &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
435     &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
436     &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
437     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
438     &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
439     my %r = ();
440     for (@{$res->result}) { $r{$_} = 1 }
442     if (not &check_opsi_res($res)){
444         if (defined $hostId){
446             $callobj = {
447                 method  => 'getProductStates_hash',
448                 params  => [ $hostId ],
449                 id  => 1,
450             };
452             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
453             if (not &check_opsi_res($hres)){
454                 my $htmp= $hres->result->{$hostId};
456                 # check state != not_installed or action == setup -> load and add
457                 foreach my $product (@{$htmp}){
459                     if (!defined ($r{$product->{'productId'}})){
460                         next;
461                     }
463                     # Now we've a couple of hashes...
464                     if ($product->{'installationStatus'} ne "not_installed" or
465                             $product->{'actionRequest'} eq "setup"){
466                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
468                         $callobj = {
469                             method  => 'getProduct_hash',
470                             params  => [ $product->{'productId'} ],
471                             id  => 1,
472                         };
474                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
475                         if (not &check_opsi_res($sres)){
476                             my $tres= $sres->result;
478                             my $name= xml_quote($tres->{'name'});
479                             my $r= $product->{'productId'};
480                             my $description= xml_quote($tres->{'description'});
481                             $name=~ s/\//\\\//;
482                             $description=~ s/\//\\\//;
483                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
484                         }
485                     }
486                 }
488             }
490         } else {
491             foreach my $r (@{$res->result}) {
492                 $callobj = {
493                     method  => 'getProduct_hash',
494                     params  => [ $r ],
495                     id  => 1,
496                 };
498                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
499                 if (not &check_opsi_res($sres)){
500                     my $tres= $sres->result;
502                     my $name= xml_quote($tres->{'name'});
503                     my $description= xml_quote($tres->{'description'});
504                     $name=~ s/\//\\\//;
505                     $description=~ s/\//\\\//;
506                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
507                 }
508             }
510         }
511     }
512     $xml_msg=~ s/<xxx><\/xxx>//;
514     # Return message
515     return ( $xml_msg );
519 ## @method opsi_get_product_properties
520 # Get product properties for a product and a specific host or gobally for a product.
521 # @param msg - STRING - xml message with tags productId and optional hostId
522 # @param msg_hash - HASHREF - message information parsed into a hash
523 # @param session_id - INTEGER - POE session id of the processing of this message
524 # @return out_msg - STRING - feedback to GOsa in success and error case
525 sub opsi_get_product_properties {
526     my ($msg, $msg_hash, $session_id) = @_;
527     my $header = @{$msg_hash->{'header'}}[0];
528     my $source = @{$msg_hash->{'source'}}[0];
529     my $target = @{$msg_hash->{'target'}}[0];
530     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
531     my ($hostId, $productId);
532     my $xml_msg;
534     # Build return message with twisted target and source
535     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
536     if (defined $forward_to_gosa) {
537         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
538     }
540     # Sanity check of needed parameter
541     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
542         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
543         &add_content2xml_hash($out_hash, "error", "productId");
544         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
546         # Return message
547         return ( &create_xml_string($out_hash) );
548     }
550     # Get productid
551     $productId = @{$msg_hash->{'productId'}}[0];
552     &add_content2xml_hash($out_hash, "producId", "$productId");
554     # Get hostId if defined
555     if (defined @{$msg_hash->{'hostId'}}[0]){
556       $hostId = @{$msg_hash->{'hostId'}}[0];
557       &add_content2xml_hash($out_hash, "hostId", $hostId);
558     }
560     # Load actions
561     my $callobj = {
562       method  => 'getPossibleProductActions_list',
563       params  => [ $productId ],
564       id  => 1,
565     };
566     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
567     if (not &check_opsi_res($res)){
568       foreach my $action (@{$res->result}){
569         &add_content2xml_hash($out_hash, "action", $action);
570       }
571     }
573     # Add place holder
574     &add_content2xml_hash($out_hash, "xxx", "");
576     # Move to XML string
577     $xml_msg= &create_xml_string($out_hash);
579     # JSON Query
580     if (defined $hostId){
581       $callobj = {
582           method  => 'getProductProperties_hash',
583           params  => [ $productId, $hostId ],
584           id  => 1,
585       };
586     } else {
587       $callobj = {
588           method  => 'getProductProperties_hash',
589           params  => [ $productId ],
590           id  => 1,
591       };
592     }
593     $res = $main::opsi_client->call($main::opsi_url, $callobj);
595     # JSON Query 2
596     $callobj = {
597       method  => 'getProductPropertyDefinitions_listOfHashes',
598       params  => [ $productId ],
599       id  => 1,
600     };
602     # Assemble options
603     my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
604     my $values = {};
605     my $descriptions = {};
606     if (not &check_opsi_res($res2)){
607         my $r= $res2->result;
609           foreach my $entr (@$r){
610             # Unroll values
611             my $cnv;
612             if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
613               foreach my $v (@{$entr->{'values'}}){
614                 $cnv.= "<value>$v</value>";
615               }
616             } else {
617               $cnv= $entr->{'values'};
618             }
619             $values->{$entr->{'name'}}= $cnv;
620             $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
621           }
622     }
624     if (not &check_opsi_res($res)){
625         my $r= $res->result;
626         foreach my $key (keys %{$r}) {
627             my $item= "\n<item>";
628             my $value= $r->{$key};
629             my $dsc= "";
630             my $vals= "";
631             if (defined $descriptions->{$key}){
632               $dsc= $descriptions->{$key};
633             }
634             if (defined $values->{$key}){
635               $vals= $values->{$key};
636             }
637             $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
638             $item.= "</item>";
639             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
640         }
641     }
643     $xml_msg=~ s/<xxx><\/xxx>//;
645     # Return message
646     return ( $xml_msg );
650 ## @method opsi_set_product_properties
651 # Set product properities for a specific host or globaly. Message needs one xml tag 'item' and within one xml tag 'name' and 'value'. The xml tags action and state are optional.
652 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
653 # @param msg_hash - HASHREF - message information parsed into a hash
654 # @param session_id - INTEGER - POE session id of the processing of this message
655 # @return out_msg - STRING - feedback to GOsa in success and error case
656 sub opsi_set_product_properties {
657     my ($msg, $msg_hash, $session_id) = @_;
658     my $header = @{$msg_hash->{'header'}}[0];
659     my $source = @{$msg_hash->{'source'}}[0];
660     my $target = @{$msg_hash->{'target'}}[0];
661     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
662     my ($productId, $hostId);
664     # Build return message with twisted target and source
665     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
666     if (defined $forward_to_gosa) {
667         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
668     }
670     # Sanity check of needed parameter
671     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
672         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
673         &add_content2xml_hash($out_hash, "error", "productId");
674         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
675         return ( &create_xml_string($out_hash) );
676     }
677     if (not exists $msg_hash->{'item'}) {
678         &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
679         &add_content2xml_hash($out_hash, "error", "item");
680         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
681         return ( &create_xml_string($out_hash) );
682     } else {
683         if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
684             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
685             &add_content2xml_hash($out_hash, "error", "name");
686             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
687             return ( &create_xml_string($out_hash) );
688         }
689         if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
690             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
691             &add_content2xml_hash($out_hash, "error", "value");
692             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
693             return ( &create_xml_string($out_hash) );
694         }
695     }
696     # if no hostId is given, set_product_properties will act on globally
697     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
698         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
699         &add_content2xml_hash($out_hash, "error", "hostId");
700         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
701         return ( &create_xml_string($out_hash) );
702     }
704         
705     # Get productId
706     $productId =  @{$msg_hash->{'productId'}}[0];
707     &add_content2xml_hash($out_hash, "productId", $productId);
709     # Get hostId if defined
710     if (exists $msg_hash->{'hostId'}){
711         $hostId = @{$msg_hash->{'hostId'}}[0];
712         &add_content2xml_hash($out_hash, "hostId", $hostId);
713     }
715     # Set product states if requested
716     if (defined @{$msg_hash->{'action'}}[0]){
717         &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
718     }
719     if (defined @{$msg_hash->{'state'}}[0]){
720         &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
721     }
723     # Find properties
724     foreach my $item (@{$msg_hash->{'item'}}){
725         # JSON Query
726         my $callobj;
728         if (defined $hostId){
729             $callobj = {
730                 method  => 'setProductProperty',
731                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
732                 id  => 1,
733             };
734         } else {
735             $callobj = {
736                 method  => 'setProductProperty',
737                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
738                 id  => 1,
739             };
740         }
742         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
743         my ($res_err, $res_err_string) = &check_opsi_res($res);
745         if ($res_err){
746             &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
747             &add_content2xml_hash($out_hash, "error", $res_err_string);
748         }
749     }
752     # Return message
753     return ( &create_xml_string($out_hash) );
757 ## @method opsi_get_client_hardware
758 # Reports client hardware inventory.
759 # @param msg - STRING - xml message with tag hostId
760 # @param msg_hash - HASHREF - message information parsed into a hash
761 # @param session_id - INTEGER - POE session id of the processing of this message
762 # @return out_msg - STRING - feedback to GOsa in success and error case
763 sub opsi_get_client_hardware {
764     my ($msg, $msg_hash, $session_id) = @_;
765     my $header = @{$msg_hash->{'header'}}[0];
766     my $source = @{$msg_hash->{'source'}}[0];
767     my $target = @{$msg_hash->{'target'}}[0];
768     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
769     my $hostId;
770     my $error = 0;
771     my $xml_msg;
773     # Build return message with twisted target and source
774     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
775     if (defined $forward_to_gosa) {
776       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
777     }
779     # Sanity check of needed parameter
780     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
781         $error++;
782         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
783         &add_content2xml_hash($out_hash, "error", "hostId");
784         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
785     }
787     if (not $error) {
789     # Get hostId
790         $hostId = @{$msg_hash->{'hostId'}}[0];
791         &add_content2xml_hash($out_hash, "hostId", "$hostId");
792         &add_content2xml_hash($out_hash, "xxx", "");
793     }    
795     # Move to XML string
796     $xml_msg= &create_xml_string($out_hash);
797     
798     if (not $error) {
800     # JSON Query
801         my $callobj = {
802             method  => 'getHardwareInformation_hash',
803             params  => [ $hostId ],
804             id  => 1,
805         };
807         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
808         if (not &check_opsi_res($res)){
809             my $result= $res->result;
810             if (ref $result eq "HASH") {
811                 foreach my $r (keys %{$result}){
812                     my $item= "\n<item><id>".xml_quote($r)."</id>";
813                     my $value= $result->{$r};
814                     foreach my $sres (@{$value}){
816                         foreach my $dres (keys %{$sres}){
817                             if (defined $sres->{$dres}){
818                                 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
819                             }
820                         }
822                     }
823                     $item.= "</item>";
824                     $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
826                 }
827             }
828         }
830         $xml_msg=~ s/<xxx><\/xxx>//;
832     }
834     # Return message
835     return ( $xml_msg );
839 ## @method opsi_list_clients
840 # Reports all Opsi clients. 
841 # @param msg - STRING - xml message 
842 # @param msg_hash - HASHREF - message information parsed into a hash
843 # @param session_id - INTEGER - POE session id of the processing of this message
844 # @return out_msg - STRING - feedback to GOsa in success and error case
845 sub opsi_list_clients {
846     my ($msg, $msg_hash, $session_id) = @_;
847     my $header = @{$msg_hash->{'header'}}[0];
848     my $source = @{$msg_hash->{'source'}}[0];
849     my $target = @{$msg_hash->{'target'}}[0];
850     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
852     # Build return message with twisted target and source
853     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
854     if (defined $forward_to_gosa) {
855       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
856     }
857     &add_content2xml_hash($out_hash, "xxx", "");
859     # Move to XML string
860     my $xml_msg= &create_xml_string($out_hash);
862     # JSON Query
863     my $callobj = {
864         method  => 'getClients_listOfHashes',
865         params  => [ ],
866         id  => 1,
867     };
868     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
869     if (not &check_opsi_res($res)){
870         foreach my $host (@{$res->result}){
871             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
872             if (defined($host->{'description'})){
873                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
874             }
875             if (defined($host->{'notes'})){
876                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
877             }
878             if (defined($host->{'lastSeen'})){
879                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
880             }
882             $callobj = {
883               method  => 'getIpAddress',
884               params  => [ $host->{'hostId'} ],
885               id  => 1,
886             };
887             my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
888             if ( not &check_opsi_res($sres)){
889               $item.= "<ip>".xml_quote($sres->result)."</ip>";
890             }
892             $callobj = {
893               method  => 'getMacAddress',
894               params  => [ $host->{'hostId'} ],
895               id  => 1,
896             };
897             $sres= $main::opsi_client->call($main::opsi_url, $callobj);
898             if ( not &check_opsi_res($sres)){
899                 $item.= "<mac>".xml_quote($sres->result)."</mac>";
900             }
901             $item.= "</item>";
902             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
903         }
904     }
906     $xml_msg=~ s/<xxx><\/xxx>//;
907     return ( $xml_msg );
912 ## @method opsi_get_client_software
913 # Reports client software inventory.
914 # @param msg - STRING - xml message with tag hostId
915 # @param msg_hash - HASHREF - message information parsed into a hash
916 # @param session_id - INTEGER - POE session id of the processing of this message
917 # @return out_msg - STRING - feedback to GOsa in success and error case
918 sub opsi_get_client_software {
919     my ($msg, $msg_hash, $session_id) = @_;
920     my $header = @{$msg_hash->{'header'}}[0];
921     my $source = @{$msg_hash->{'source'}}[0];
922     my $target = @{$msg_hash->{'target'}}[0];
923     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
924     my $error = 0;
925     my $hostId;
926     my $xml_msg;
928     # Build return message with twisted target and source
929     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
930     if (defined $forward_to_gosa) {
931       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
932     }
934     # Sanity check of needed parameter
935     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
936         $error++;
937         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
938         &add_content2xml_hash($out_hash, "error", "hostId");
939         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
940     }
942     if (not $error) {
944     # Get hostId
945         $hostId = @{$msg_hash->{'hostId'}}[0];
946         &add_content2xml_hash($out_hash, "hostId", "$hostId");
947         &add_content2xml_hash($out_hash, "xxx", "");
948     }
950     $xml_msg= &create_xml_string($out_hash);
952     if (not $error) {
954     # JSON Query
955         my $callobj = {
956             method  => 'getSoftwareInformation_hash',
957             params  => [ $hostId ],
958             id  => 1,
959         };
961         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
962         if (not &check_opsi_res($res)){
963             my $result= $res->result;
964 # TODO : Ist das hier schon fertig???   
965         }
967         $xml_msg=~ s/<xxx><\/xxx>//;
969     }
971     # Return message
972     return ( $xml_msg );
976 ## @method opsi_get_local_products
977 # Reports product for given hostId or globally.
978 # @param msg - STRING - xml message with optional tag hostId
979 # @param msg_hash - HASHREF - message information parsed into a hash
980 # @param session_id - INTEGER - POE session id of the processing of this message
981 # @return out_msg - STRING - feedback to GOsa in success and error case
982 sub opsi_get_local_products {
983     my ($msg, $msg_hash, $session_id) = @_;
984     my $header = @{$msg_hash->{'header'}}[0];
985     my $source = @{$msg_hash->{'source'}}[0];
986     my $target = @{$msg_hash->{'target'}}[0];
987     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
988     my $hostId;
990     # Build return message with twisted target and source
991     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
992     if (defined $forward_to_gosa) {
993         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
994     }
995     &add_content2xml_hash($out_hash, "xxx", "");
997     # Get hostId if defined
998     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
999         $hostId = @{$msg_hash->{'hostId'}}[0];
1000         &add_content2xml_hash($out_hash, "hostId", $hostId);
1001     }
1003     # Move to XML string
1004     my $xml_msg= &create_xml_string($out_hash);
1006     # For hosts, only return the products that are or get installed
1007     my $callobj;
1008     $callobj = {
1009         method  => 'getLocalBootProductIds_list',
1010         params  => [ ],
1011         id  => 1,
1012     };
1014     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1015     my %r = ();
1016     for (@{$res->result}) { $r{$_} = 1 }
1018     if (not &check_opsi_res($res)){
1020         if (defined $hostId){
1021             $callobj = {
1022                 method  => 'getProductStates_hash',
1023                 params  => [ $hostId ],
1024                 id  => 1,
1025             };
1027             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1028             if (not &check_opsi_res($hres)){
1029                 my $htmp= $hres->result->{$hostId};
1031                 # Check state != not_installed or action == setup -> load and add
1032                 foreach my $product (@{$htmp}){
1034                     if (!defined ($r{$product->{'productId'}})){
1035                         next;
1036                     }
1038                     # Now we've a couple of hashes...
1039                     if ($product->{'installationStatus'} ne "not_installed" or
1040                             $product->{'actionRequest'} eq "setup"){
1041                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1043                         $callobj = {
1044                             method  => 'getProduct_hash',
1045                             params  => [ $product->{'productId'} ],
1046                             id  => 1,
1047                         };
1049                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1050                         if (not &check_opsi_res($sres)){
1051                             my $tres= $sres->result;
1053                             my $name= xml_quote($tres->{'name'});
1054                             my $r= $product->{'productId'};
1055                             my $description= xml_quote($tres->{'description'});
1056                             $name=~ s/\//\\\//;
1057                             $description=~ s/\//\\\//;
1058                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1059                         }
1061                     }
1062                 }
1064             }
1066         } else {
1067             foreach my $r (@{$res->result}) {
1068                 $callobj = {
1069                     method  => 'getProduct_hash',
1070                     params  => [ $r ],
1071                     id  => 1,
1072                 };
1074                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1075                 if (not &check_opsi_res($sres)){
1076                     my $tres= $sres->result;
1078                     my $name= xml_quote($tres->{'name'});
1079                     my $description= xml_quote($tres->{'description'});
1080                     $name=~ s/\//\\\//;
1081                     $description=~ s/\//\\\//;
1082                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1083                 }
1085             }
1087         }
1088     }
1090     $xml_msg=~ s/<xxx><\/xxx>//;
1092     # Retrun Message
1093     return ( $xml_msg );
1097 ## @method opsi_del_client
1098 # Deletes a client from Opsi.
1099 # @param msg - STRING - xml message with tag hostId
1100 # @param msg_hash - HASHREF - message information parsed into a hash
1101 # @param session_id - INTEGER - POE session id of the processing of this message
1102 # @return out_msg - STRING - feedback to GOsa in success and error case
1103 sub opsi_del_client {
1104     my ($msg, $msg_hash, $session_id) = @_;
1105     my $header = @{$msg_hash->{'header'}}[0];
1106     my $source = @{$msg_hash->{'source'}}[0];
1107     my $target = @{$msg_hash->{'target'}}[0];
1108     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1109     my $hostId;
1110     my $error = 0;
1112     # Build return message with twisted target and source
1113     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1114     if (defined $forward_to_gosa) {
1115       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1116     }
1118     # Sanity check of needed parameter
1119     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1120         $error++;
1121         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1122         &add_content2xml_hash($out_hash, "error", "hostId");
1123         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1124     }
1126     if (not $error) {
1128     # Get hostId
1129         $hostId = @{$msg_hash->{'hostId'}}[0];
1130         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1132     # JSON Query
1133         my $callobj = {
1134             method  => 'deleteClient',
1135             params  => [ $hostId ],
1136             id  => 1,
1137         };
1138         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1139     }
1141     # Move to XML string
1142     my $xml_msg= &create_xml_string($out_hash);
1144     # Return message
1145     return ( $xml_msg );
1149 ## @method opsi_install_client
1150 # Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1151 # @param msg - STRING - xml message with tags hostId, macaddress
1152 # @param msg_hash - HASHREF - message information parsed into a hash
1153 # @param session_id - INTEGER - POE session id of the processing of this message
1154 # @return out_msg - STRING - feedback to GOsa in success and error case
1155 sub opsi_install_client {
1156     my ($msg, $msg_hash, $session_id) = @_;
1157     my $header = @{$msg_hash->{'header'}}[0];
1158     my $source = @{$msg_hash->{'source'}}[0];
1159     my $target = @{$msg_hash->{'target'}}[0];
1160     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1163     my ($hostId, $macaddress);
1165     my $error = 0;
1166     my @out_msg_l;
1168     # Build return message with twisted target and source
1169     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1170     if (defined $forward_to_gosa) {
1171         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1172     }
1174     # Sanity check of needed parameter
1175     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1176         $error++;
1177         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1178         &add_content2xml_hash($out_hash, "error", "hostId");
1179         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1180     }
1181     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
1182         $error++;
1183         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1184         &add_content2xml_hash($out_hash, "error", "macaddress");
1185         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1186     } else {
1187         if ((exists $msg_hash->{'macaddress'}) && 
1188                 ($msg_hash->{'macaddress'}[0] =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})$/i)) {  
1189             $macaddress = $1; 
1190         } else { 
1191             $error ++; 
1192             &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1193             &add_content2xml_hash($out_hash, "error", "macaddress");
1194             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1195         }
1196     }
1198     if (not $error) {
1200     # Get hostId
1201         $hostId = @{$msg_hash->{'hostId'}}[0];
1202         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1204         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1205         my $callobj = {
1206             method  => 'getProductStates_hash',
1207             params  => [ $hostId ],
1208             id  => 1,
1209         };
1211         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1212         if (not &check_opsi_res($hres)){
1213             my $htmp= $hres->result->{$hostId};
1215             # check state != not_installed or action == setup -> load and add
1216             foreach my $product (@{$htmp}){
1217                 # Now we've a couple of hashes...
1218                 if ($product->{'installationStatus'} ne "not_installed" or
1219                         $product->{'actionRequest'} ne "none"){
1221                     # Do an action request for all these -> "setup".
1222                     $callobj = {
1223                         method  => 'setProductActionRequest',
1224                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1225                         id  => 1,
1226                     };
1227                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1228                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1229                     if ($res_err){
1230                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1231                     } else {
1232                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1233                     }
1234                 }
1235             }
1236         }
1237         push(@out_msg_l, &create_xml_string($out_hash));
1238     
1240     # Build wakeup message for client
1241         if (not $error) {
1242             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1243             &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1244             my $wakeup_msg = &create_xml_string($wakeup_hash);
1245             push(@out_msg_l, $wakeup_msg);
1247             # invoke trigger wake for this gosa-si-server
1248             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1249         }
1250     }
1251     
1252     # Return messages
1253     return @out_msg_l;
1257 ## @method _set_action
1258 # Set action for an Opsi client
1259 # @param product - STRING - Opsi product
1260 # @param action - STRING - action
1261 # @param hostId - STRING - Opsi hostId
1262 sub _set_action {
1263   my $product= shift;
1264   my $action = shift;
1265   my $hostId = shift;
1266   my $callobj;
1268   $callobj = {
1269     method  => 'setProductActionRequest',
1270     params  => [ $product, $hostId, $action],
1271     id  => 1,
1272   };
1274   $main::opsi_client->call($main::opsi_url, $callobj);
1277 ## @method _set_state
1278 # Set state for an Opsi client
1279 # @param product - STRING - Opsi product
1280 # @param action - STRING - state
1281 # @param hostId - STRING - Opsi hostId
1282 sub _set_state {
1283   my $product = shift;
1284   my $state = shift;
1285   my $hostId = shift;
1286   my $callobj;
1288   $callobj = {
1289     method  => 'setProductState',
1290     params  => [ $product, $hostId, $state ],
1291     id  => 1,
1292   };
1294   $main::opsi_client->call($main::opsi_url, $callobj);
1297 ################################
1299 # @brief Create a license pool at Opsi server.
1301 sub opsi_createLicensePool {
1302     my ($msg, $msg_hash, $session_id) = @_;
1303     my $header = @{$msg_hash->{'header'}}[0];
1304     my $source = @{$msg_hash->{'source'}}[0];
1305     my $target = @{$msg_hash->{'target'}}[0];
1306         my $out_hash;
1307         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1308         my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1309         my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1310         my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1312         # Submit data to Opsi server
1313     my $callobj = {
1314         method  => 'createLicensePool',
1315         params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1316         id  => 1,
1317     };
1318     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1320         # Check Opsi error
1321         my ($res_error, $res_error_str) = &check_opsi_res($res);
1322         if ($res_error){
1323                 # Create error message
1324                 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1325                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1327         } else {
1328                 # Create function result message
1329                 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1330         }
1332         return ( &create_xml_string($out_hash) );
1335 ################################
1337 # @brief Fetch license pool IDs from Opsi server and provide it as a list.
1339 sub opsi_getLicensePoolIds_list {
1340     my ($msg, $msg_hash, $session_id) = @_;
1341     my $header = @{$msg_hash->{'header'}}[0];
1342     my $source = @{$msg_hash->{'source'}}[0];
1343         my $out_hash;
1345         # Fetch infos from Opsi server
1346     my $callobj = {
1347         method  => 'getLicensePoolIds_list',
1348         params  => [ ],
1349         id  => 1,
1350     };
1351     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1353         # Check Opsi error
1354         my ($res_error, $res_error_str) = &check_opsi_res($res);
1355         if ($res_error){
1356                 # Create error message
1357                 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1358                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1360         } else {
1361                 # Create function result message
1362                 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1363                 map(&add_content2xml_hash($out_hash, "licensePoolIds", "$_"), @{$res->result});
1364         }
1366         return ( &create_xml_string($out_hash) );
1370 ################################
1372 # @brief Fetch license pool details(productIds, windowsSoftwareIds, description) from Opsi server and 
1374 sub opsi_getLicensePool_hash {
1375     my ($msg, $msg_hash, $session_id) = @_;
1376     my $header = @{$msg_hash->{'header'}}[0];
1377     my $source = @{$msg_hash->{'source'}}[0];
1378     my $target = @{$msg_hash->{'target'}}[0];
1379     my $licensePoolId;
1380         my $out_hash;
1382         # Check input sanity
1383         if (not defined $msg_hash->{'licensePoolId'}) {
1384                 &main::daemon_log("$session_id ERROR: message contains no tag 'licensePoolId': ".$msg, 1);
1385                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, "message contains no tag 'licensePoolId'");
1386                 return ( &create_xml_string($out_hash) );
1387         }
1388         if (ref @{$msg_hash->{'licensePoolId'}}[0] eq 'HASH') {
1389                 &main::daemon_log("$session_id ERROR: message contains no license pool ID: ".$msg, 1);
1390                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, "message contains no license pool ID");
1391                 return ( &create_xml_string($out_hash) );
1392         }
1393         $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1395         # Fetch infos from Opsi server
1396     my $callobj = {
1397         method  => 'getLicensePool_hash',
1398         params  => [ $licensePoolId ],
1399         id  => 1,
1400     };
1401     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1403         # Check Opsi error
1404         my ($res_error, $res_error_str) = &check_opsi_res($res);
1405         if ($res_error){
1406                 # Create error message
1407                 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1408                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1409                 &add_content2xml_hash($out_hash, "error", $res_error_str);
1411         } else {
1412                 # Create function result message
1413                 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1414                 &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1415                 &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1416                 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1417                 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1418         }
1420         return ( &create_xml_string($out_hash) );
1423 ################################
1425 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
1426 # by specifing the parameter deleteLicenses=True all software licenses bound to the pool are being deleted. 
1428 # TODO: funktion loescht alles bis auf die lizenz vertraege, weg finden wie auch noch die verträge gelöscht werden könne (getSoftwareLicenses_listOfHashes)
1429 sub opsi_deleteLicensePool {
1430         my ($msg, $msg_hash, $session_id) = @_;
1431     my $header = @{$msg_hash->{'header'}}[0];
1432     my $source = @{$msg_hash->{'source'}}[0];
1433     my $target = @{$msg_hash->{'target'}}[0];
1434     my $licensePoolId;
1435         my $out_hash;
1437         # Check input sanity
1438         if (not defined $msg_hash->{'licensePoolId'}) {
1439                 &main::daemon_log("$session_id ERROR: message contains no tag 'licensePoolId': ".$msg, 1);
1440                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, "message contains no tag 'licensePoolId'");
1441                 return ( &create_xml_string($out_hash) );
1442         }
1443         if (ref @{$msg_hash->{'licensePoolId'}}[0] eq 'HASH') {
1444                 &main::daemon_log("$session_id ERROR: message contains no license pool ID: ".$msg, 1);
1445                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, "message contains no license pool ID");
1446                 return ( &create_xml_string($out_hash) );
1447         }
1448         $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1450         # Fetch infos from Opsi server
1451     my $callobj = {
1452         method  => 'deleteLicensePool',
1453         params  => [ $licensePoolId, 'deleteLicenses=True'  ],
1454         id  => 1,
1455     };
1456     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1458         # Check Opsi error
1459         my ($res_error, $res_error_str) = &check_opsi_res($res);
1460         if ($res_error){
1461                 # Create error message
1462                 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1463                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1465         } else {
1466                 # Create function result message
1467                 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1468         }
1470         return ( &create_xml_string($out_hash) );
1474 ################################
1476 # @brief 
1478 sub opsi_createLicense {
1479         my ($msg, $msg_hash, $session_id) = @_;
1480     my $header = @{$msg_hash->{'header'}}[0];
1481     my $source = @{$msg_hash->{'source'}}[0];
1482     my $target = @{$msg_hash->{'target'}}[0];
1483         my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1484         my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1485         my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1486         my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1487         my $licenseContractId;
1488         my $softwareLicenseId = defined $msg_hash->{'licenseId'} ? @{$msg_hash->{'licenseId'}}[0] : undef;
1489         my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1490         my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1491         my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1492         my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1493         my $out_hash;
1495         # Check input sanity
1496         if (not defined $msg_hash->{'licensePoolId'}) {
1497                 &main::daemon_log("$session_id ERROR: message contains no tag 'licensePoolId': ".$msg, 1);
1498                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, "message contains no tag 'licensePoolId'");
1499                 return ( &create_xml_string($out_hash) );
1500         }
1501         if (ref @{$msg_hash->{'licensePoolId'}}[0] eq 'HASH') {
1502                 &main::daemon_log("$session_id ERROR: message contains no license pool ID: ".$msg, 1);
1503                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, "message contains no license pool ID");
1504                 return ( &create_xml_string($out_hash) );
1505         }
1506         my $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1507         if (not defined $msg_hash->{'licenseKey'}) {
1508                 &main::daemon_log("$session_id ERROR: message contains no tag 'licenseKey': ".$msg, 1);
1509                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, "message contains no tag 'licenseKey'");
1510                 return ( &create_xml_string($out_hash) );
1511         }
1512         if (ref @{$msg_hash->{'licenseKey'}}[0] eq 'HASH') {
1513                 &main::daemon_log("$session_id ERROR: message contains no license key: ".$msg, 1);
1514                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, "message contains no license key");
1515                 return ( &create_xml_string($out_hash) );
1516         }
1517         my $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1519         # Create license contract at Opsi server
1520     my $callobj = {
1521         method  => 'createLicenseContract',
1522         params  => [ undef, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1523         id  => 1,
1524     };
1525     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1527         # Check Opsi error
1528         my ($res_error, $res_error_str) = &check_opsi_res($res);
1529         if ($res_error){
1530 print STDERR Dumper $res;       
1531                 # Create error message
1532                 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1533                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1534                 return ( &create_xml_string($out_hash) );
1535         }
1536         
1537         $licenseContractId = $res->result;
1538 print STDERR Dumper $licenseContractId; 
1540         # Create software license at Opsi server
1541     $callobj = {
1542         method  => 'createSoftwareLicense',
1543         params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1544         id  => 1,
1545     };
1546     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1548         # Check Opsi error
1549         ($res_error, $res_error_str) = &check_opsi_res($res);
1550         if ($res_error){
1551 print STDERR Dumper $res;       
1552                 # Create error message
1553                 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1554                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1555                 return ( &create_xml_string($out_hash) );
1556         }
1557         $softwareLicenseId = $res->result;
1558 print STDERR Dumper $softwareLicenseId;
1560         # Add software license to license pool
1561         $callobj = {
1562         method  => 'addSoftwareLicenseToLicensePool',
1563         params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1564         id  => 1,
1565     };
1566     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1568         # Check Opsi error
1569         ($res_error, $res_error_str) = &check_opsi_res($res);
1570         if ($res_error){
1571 print STDERR Dumper $res;       
1572                 # Create error message
1573                 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1574                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1575                 return ( &create_xml_string($out_hash) );
1576         }
1577 print STDERR Dumper $res;
1581 ## Create license contract
1582 #licenseContractId
1583 #partner
1584 #conclusionDate
1585 #notificationDate
1586 #expirationDate
1587 #notes
1589 ## Create software license
1590 #softwareLicenseId, 
1591 #licenseContractId, 
1592 #licenseType, 
1593 #maxInstallations, 
1594 #boundToHost, 
1595 #expirationDate
1596         return;
1599 1;