Code

two new functionalities for gosa-si plugin opsi_com.pm: opsi_boundHostToLicense and...
[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_deleteLicensePool",
26         "opsi_createLicense",
27         "opsi_assignSoftwareLicenseToHost",
28         "opsi_unassignSoftwareLicenseFromHost",
29         "opsi_unassignAllSoftwareLicensesFromHost",
30         "opsi_getSoftwareLicense_hash",
31         "opsi_getLicensePool_hash",
32         "opsi_getSoftwareLicenseUsages",
33         "opsi_getSoftwareLicenseUsagesForProductId",
34         "opsi_getLicensePools_listOfHashes",
35         "opsi_getLicenseInformationForProduct",
36         "opsi_getPool",
37         "opsi_removeLicense",
38         "opsi_getReservedLicenses",
39         "opsi_boundHostToLicense",
40         "opsi_unboundHostFromLicense",
41         "opsi_test",
42    );
43 @EXPORT = @events;
45 use strict;
46 use warnings;
47 use GOSA::GosaSupportDaemon;
48 use Data::Dumper;
49 use XML::Quote qw(:all);
51 BEGIN {}
53 END {}
55 # ----------------------------------------------------------------------------
56 #                          D E C L A R A T I O N S
57 # ----------------------------------------------------------------------------
59 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
63 # ----------------------------------------------------------------------------
64 #                            S U B R O U T I N E S
65 # ----------------------------------------------------------------------------
68 ################################
69 #
70 # @brief A function returning a list of functions which are exported by importing the module.
71 # @return List of all provided functions
72 #
73 sub get_events {
74     return \@events;
75 }
77 ################################
78 #
79 # @brief Checks if there is a specified tag and if the the tag has a content.
80 # @return 0|1
81 #
82 sub _check_xml_tag_is_ok {
83         my ($msg_hash,$tag) = @_;
84         if (not defined $msg_hash->{$tag}) {
85                 $_ = "message contains no tag '$tag'";
86                 return 0;
87         }
88         if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
89                 $_ = "message tag '$tag' has no content";
90                 return  0;
91         }
92         return 1;
93 }
95 ################################
96 #
97 # @brief Writes the log line and returns the error message for GOsa.
98 #
99 sub _give_feedback {
100         my ($msg, $msg_hash, $session_id, $error) = @_;
101         &main::daemon_log("$session_id ERROR: $error: ".$msg, 1);
102         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{'source'}}[0], $error);
103         return &create_xml_string($out_hash);
106 ## @method opsi_add_product_to_client
107 # Adds an Opsi product to an Opsi client.
108 # @param msg - STRING - xml message with tags hostId and productId
109 # @param msg_hash - HASHREF - message information parsed into a hash
110 # @param session_id - INTEGER - POE session id of the processing of this message
111 # @return out_msg - STRING - feedback to GOsa in success and error case
112 sub opsi_add_product_to_client {
113     my ($msg, $msg_hash, $session_id) = @_;
114     my $header = @{$msg_hash->{'header'}}[0];
115     my $source = @{$msg_hash->{'source'}}[0];
116     my $target = @{$msg_hash->{'target'}}[0];
117     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
118     my ($hostId, $productId);
119     my $error = 0;
121     # Build return message
122     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
123     if (defined $forward_to_gosa) {
124         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
125     }
127     # Sanity check of needed parameter
128     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
129         $error++;
130         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
131         &add_content2xml_hash($out_hash, "error", "hostId");
132         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
134     }
135     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
136         $error++;
137         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
138         &add_content2xml_hash($out_hash, "error", "productId");
139         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
140     }
142     if (not $error) {
143         # Get hostId
144         $hostId = @{$msg_hash->{'hostId'}}[0];
145         &add_content2xml_hash($out_hash, "hostId", $hostId);
147         # Get productID
148         $productId = @{$msg_hash->{'productId'}}[0];
149         &add_content2xml_hash($out_hash, "productId", $productId);
151         # Do an action request for all these -> "setup".
152         my $callobj = {
153             method  => 'setProductActionRequest',
154             params  => [ $productId, $hostId, "setup" ],
155             id  => 1, }; 
157         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
158         my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
159         if ($sres_err){
160             &main::daemon_log("$session_id ERROR: cannot add product: ".$sres_err_string, 1);
161             &add_content2xml_hash($out_hash, "error", $sres_err_string);
162         }
163     } 
165     # return message
166     return ( &create_xml_string($out_hash) );
169 ## @method opsi_del_product_from_client
170 # Deletes an Opsi-product from an Opsi-client. 
171 # @param msg - STRING - xml message with tags hostId and productId
172 # @param msg_hash - HASHREF - message information parsed into a hash
173 # @param session_id - INTEGER - POE session id of the processing of this message
174 # @return out_msg - STRING - feedback to GOsa in success and error case
175 sub opsi_del_product_from_client {
176     my ($msg, $msg_hash, $session_id) = @_;
177     my $header = @{$msg_hash->{'header'}}[0];
178     my $source = @{$msg_hash->{'source'}}[0];
179     my $target = @{$msg_hash->{'target'}}[0];
180     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
181     my ($hostId, $productId);
182     my $error = 0;
183     my ($sres, $sres_err, $sres_err_string);
185     # Build return message
186     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
187     if (defined $forward_to_gosa) {
188         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
189     }
191     # Sanity check of needed parameter
192     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
193         $error++;
194         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
195         &add_content2xml_hash($out_hash, "error", "hostId");
196         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
198     }
199     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
200         $error++;
201         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
202         &add_content2xml_hash($out_hash, "error", "productId");
203         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
204     }
206     # All parameter available
207     if (not $error) {
208         # Get hostId
209         $hostId = @{$msg_hash->{'hostId'}}[0];
210         &add_content2xml_hash($out_hash, "hostId", $hostId);
212         # Get productID
213         $productId = @{$msg_hash->{'productId'}}[0];
214         &add_content2xml_hash($out_hash, "productId", $productId);
217 # : check the results for more than one entry which is currently installed
218         #$callobj = {
219         #    method  => 'getProductDependencies_listOfHashes',
220         #    params  => [ $productId ],
221         #    id  => 1, };
222         #
223         #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
224         #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
225         #if ($sres_err){
226         #  &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
227         #  &add_content2xml_hash($out_hash, "error", $sres_err_string);
228         #  return ( &create_xml_string($out_hash) );
229         #}
232         # Check to get product action list 
233         my $callobj = {
234             method  => 'getPossibleProductActions_list',
235             params  => [ $productId ],
236             id  => 1, };
237         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
238         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
239         if ($sres_err){
240             &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
241             &add_content2xml_hash($out_hash, "error", $sres_err_string);
242             $error++;
243         }
244     }
246     # Check action uninstall of product
247     if (not $error) {
248         my $uninst_possible= 0;
249         foreach my $r (@{$sres->result}) {
250             if ($r eq 'uninstall') {
251                 $uninst_possible= 1;
252             }
253         }
254         if (!$uninst_possible){
255             &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
256             &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
257             $error++;
258         }
259     }
261     # Set product state to "none"
262     # Do an action request for all these -> "setup".
263     if (not $error) {
264         my $callobj = {
265             method  => 'setProductActionRequest',
266             params  => [ $productId, $hostId, "none" ],
267             id  => 1, 
268         }; 
269         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
270         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
271         if ($sres_err){
272             &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
273             &add_content2xml_hash($out_hash, "error", $sres_err_string);
274         }
275     }
277     # Return message
278     return ( &create_xml_string($out_hash) );
281 ## @method opsi_add_client
282 # Adds an Opsi client to Opsi.
283 # @param msg - STRING - xml message with tags hostId and macaddress
284 # @param msg_hash - HASHREF - message information parsed into a hash
285 # @param session_id - INTEGER - POE session id of the processing of this message
286 # @return out_msg - STRING - feedback to GOsa in success and error case
287 sub opsi_add_client {
288     my ($msg, $msg_hash, $session_id) = @_;
289     my $header = @{$msg_hash->{'header'}}[0];
290     my $source = @{$msg_hash->{'source'}}[0];
291     my $target = @{$msg_hash->{'target'}}[0];
292     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
293     my ($hostId, $mac);
294     my $error = 0;
295     my ($sres, $sres_err, $sres_err_string);
297     # Build return message with twisted target and source
298     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
299     if (defined $forward_to_gosa) {
300         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
301     }
303     # Sanity check of needed parameter
304     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
305         $error++;
306         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
307         &add_content2xml_hash($out_hash, "error", "hostId");
308         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
309     }
310     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
311         $error++;
312         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
313         &add_content2xml_hash($out_hash, "error", "macaddress");
314         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
315     }
317     if (not $error) {
318         # Get hostId
319         $hostId = @{$msg_hash->{'hostId'}}[0];
320         &add_content2xml_hash($out_hash, "hostId", $hostId);
322         # Get macaddress
323         $mac = @{$msg_hash->{'macaddress'}}[0];
324         &add_content2xml_hash($out_hash, "macaddress", $mac);
326         my $name= $hostId;
327         $name=~ s/^([^.]+).*$/$1/;
328         my $domain= $hostId;
329         $domain=~ s/^[^.]+\.(.*)$/$1/;
330         my ($description, $notes, $ip);
332         if (defined @{$msg_hash->{'description'}}[0]){
333             $description = @{$msg_hash->{'description'}}[0];
334         }
335         if (defined @{$msg_hash->{'notes'}}[0]){
336             $notes = @{$msg_hash->{'notes'}}[0];
337         }
338         if (defined @{$msg_hash->{'ip'}}[0]){
339             $ip = @{$msg_hash->{'ip'}}[0];
340         }
342         my $callobj;
343         $callobj = {
344             method  => 'createClient',
345             params  => [ $name, $domain, $description, $notes, $ip, $mac ],
346             id  => 1,
347         };
349         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
350         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
351         if ($sres_err){
352             &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
353             &add_content2xml_hash($out_hash, "error", $sres_err_string);
354         } else {
355             &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5); 
356         }
357     }
359     # Return message
360     return ( &create_xml_string($out_hash) );
363 ## @method opsi_modify_client
364 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
365 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
366 # @param msg_hash - HASHREF - message information parsed into a hash
367 # @param session_id - INTEGER - POE session id of the processing of this message    
368 # @return out_msg - STRING - feedback to GOsa in success and error case
369 sub opsi_modify_client {
370     my ($msg, $msg_hash, $session_id) = @_;
371     my $header = @{$msg_hash->{'header'}}[0];
372     my $source = @{$msg_hash->{'source'}}[0];
373     my $target = @{$msg_hash->{'target'}}[0];
374     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
375     my $hostId;
376     my $error = 0;
377     my ($sres, $sres_err, $sres_err_string);
379     # Build return message with twisted target and source
380     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
381     if (defined $forward_to_gosa) {
382         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
383     }
385     # Sanity check of needed parameter
386     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
387         $error++;
388         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
389         &add_content2xml_hash($out_hash, "error", "hostId");
390         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
391     }
393     if (not $error) {
394         # Get hostId
395         $hostId = @{$msg_hash->{'hostId'}}[0];
396         &add_content2xml_hash($out_hash, "hostId", $hostId);
397         my $name= $hostId;
398         $name=~ s/^([^.]+).*$/$1/;
399         my $domain= $hostId;
400         $domain=~ s/^[^.]+(.*)$/$1/;
402         # Modify description, notes or mac if defined
403         my ($description, $notes, $mac);
404         my $callobj;
405         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
406             $description = @{$msg_hash->{'description'}}[0];
407             $callobj = {
408                 method  => 'setHostDescription',
409                 params  => [ $hostId, $description ],
410                 id  => 1,
411             };
412             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
413             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
414             if ($sres_err){
415                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
416                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
417             }
418         }
419         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
420             $notes = @{$msg_hash->{'notes'}}[0];
421             $callobj = {
422                 method  => 'setHostNotes',
423                 params  => [ $hostId, $notes ],
424                 id  => 1,
425             };
426             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
427             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
428             if ($sres_err){
429                 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
430                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
431             }
432         }
433         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
434             $mac = @{$msg_hash->{'mac'}}[0];
435             $callobj = {
436                 method  => 'setMacAddress',
437                 params  => [ $hostId, $mac ],
438                 id  => 1,
439             };
440             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
441             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
442             if ($sres_err){
443                 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
444                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
445             }
446         }
447     }
449     # Return message
450     return ( &create_xml_string($out_hash) );
453     
454 ## @method opsi_get_netboot_products
455 # Get netboot products for specific host.
456 # @param msg - STRING - xml message with tag hostId
457 # @param msg_hash - HASHREF - message information parsed into a hash
458 # @param session_id - INTEGER - POE session id of the processing of this message
459 # @return out_msg - STRING - feedback to GOsa in success and error case
460 sub opsi_get_netboot_products {
461     my ($msg, $msg_hash, $session_id) = @_;
462     my $header = @{$msg_hash->{'header'}}[0];
463     my $source = @{$msg_hash->{'source'}}[0];
464     my $target = @{$msg_hash->{'target'}}[0];
465     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
466     my $hostId;
467     my $xml_msg;
469     # Build return message with twisted target and source
470     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
471     if (defined $forward_to_gosa) {
472         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
473     }
475     # Get hostId if defined
476     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
477         $hostId = @{$msg_hash->{'hostId'}}[0];
478         &add_content2xml_hash($out_hash, "hostId", $hostId);
479     }
481     &add_content2xml_hash($out_hash, "xxx", "");
482     $xml_msg = &create_xml_string($out_hash);
483     # For hosts, only return the products that are or get installed
484     my $callobj;
485     $callobj = {
486         method  => 'getNetBootProductIds_list',
487         params  => [ ],
488         id  => 1,
489     };
490     &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
491     &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
492     &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
493     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
494     &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
495     my %r = ();
496     for (@{$res->result}) { $r{$_} = 1 }
498     if (not &check_opsi_res($res)){
500         if (defined $hostId){
502             $callobj = {
503                 method  => 'getProductStates_hash',
504                 params  => [ $hostId ],
505                 id  => 1,
506             };
508             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
509             if (not &check_opsi_res($hres)){
510                 my $htmp= $hres->result->{$hostId};
512                 # check state != not_installed or action == setup -> load and add
513                 foreach my $product (@{$htmp}){
515                     if (!defined ($r{$product->{'productId'}})){
516                         next;
517                     }
519                     # Now we've a couple of hashes...
520                     if ($product->{'installationStatus'} ne "not_installed" or
521                             $product->{'actionRequest'} eq "setup"){
522                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
524                         $callobj = {
525                             method  => 'getProduct_hash',
526                             params  => [ $product->{'productId'} ],
527                             id  => 1,
528                         };
530                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
531                         if (not &check_opsi_res($sres)){
532                             my $tres= $sres->result;
534                             my $name= xml_quote($tres->{'name'});
535                             my $r= $product->{'productId'};
536                             my $description= xml_quote($tres->{'description'});
537                             $name=~ s/\//\\\//;
538                             $description=~ s/\//\\\//;
539                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
540                         }
541                     }
542                 }
544             }
546         } else {
547             foreach my $r (@{$res->result}) {
548                 $callobj = {
549                     method  => 'getProduct_hash',
550                     params  => [ $r ],
551                     id  => 1,
552                 };
554                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
555                 if (not &check_opsi_res($sres)){
556                     my $tres= $sres->result;
558                     my $name= xml_quote($tres->{'name'});
559                     my $description= xml_quote($tres->{'description'});
560                     $name=~ s/\//\\\//;
561                     $description=~ s/\//\\\//;
562                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
563                 }
564             }
566         }
567     }
568     $xml_msg=~ s/<xxx><\/xxx>//;
570     # Return message
571     return ( $xml_msg );
575 ## @method opsi_get_product_properties
576 # Get product properties for a product and a specific host or gobally for a product.
577 # @param msg - STRING - xml message with tags productId and optional hostId
578 # @param msg_hash - HASHREF - message information parsed into a hash
579 # @param session_id - INTEGER - POE session id of the processing of this message
580 # @return out_msg - STRING - feedback to GOsa in success and error case
581 sub opsi_get_product_properties {
582     my ($msg, $msg_hash, $session_id) = @_;
583     my $header = @{$msg_hash->{'header'}}[0];
584     my $source = @{$msg_hash->{'source'}}[0];
585     my $target = @{$msg_hash->{'target'}}[0];
586     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
587     my ($hostId, $productId);
588     my $xml_msg;
590     # Build return message with twisted target and source
591     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
592     if (defined $forward_to_gosa) {
593         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
594     }
596     # Sanity check of needed parameter
597     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
598         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
599         &add_content2xml_hash($out_hash, "error", "productId");
600         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
602         # Return message
603         return ( &create_xml_string($out_hash) );
604     }
606     # Get productid
607     $productId = @{$msg_hash->{'productId'}}[0];
608     &add_content2xml_hash($out_hash, "producId", "$productId");
610     # Get hostId if defined
611     if (defined @{$msg_hash->{'hostId'}}[0]){
612       $hostId = @{$msg_hash->{'hostId'}}[0];
613       &add_content2xml_hash($out_hash, "hostId", $hostId);
614     }
616     # Load actions
617     my $callobj = {
618       method  => 'getPossibleProductActions_list',
619       params  => [ $productId ],
620       id  => 1,
621     };
622     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
623     if (not &check_opsi_res($res)){
624       foreach my $action (@{$res->result}){
625         &add_content2xml_hash($out_hash, "action", $action);
626       }
627     }
629     # Add place holder
630     &add_content2xml_hash($out_hash, "xxx", "");
632     # Move to XML string
633     $xml_msg= &create_xml_string($out_hash);
635     # JSON Query
636     if (defined $hostId){
637       $callobj = {
638           method  => 'getProductProperties_hash',
639           params  => [ $productId, $hostId ],
640           id  => 1,
641       };
642     } else {
643       $callobj = {
644           method  => 'getProductProperties_hash',
645           params  => [ $productId ],
646           id  => 1,
647       };
648     }
649     $res = $main::opsi_client->call($main::opsi_url, $callobj);
651     # JSON Query 2
652     $callobj = {
653       method  => 'getProductPropertyDefinitions_listOfHashes',
654       params  => [ $productId ],
655       id  => 1,
656     };
658     # Assemble options
659     my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
660     my $values = {};
661     my $descriptions = {};
662     if (not &check_opsi_res($res2)){
663         my $r= $res2->result;
665           foreach my $entr (@$r){
666             # Unroll values
667             my $cnv;
668             if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
669               foreach my $v (@{$entr->{'values'}}){
670                 $cnv.= "<value>$v</value>";
671               }
672             } else {
673               $cnv= $entr->{'values'};
674             }
675             $values->{$entr->{'name'}}= $cnv;
676             $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
677           }
678     }
680     if (not &check_opsi_res($res)){
681         my $r= $res->result;
682         foreach my $key (keys %{$r}) {
683             my $item= "\n<item>";
684             my $value= $r->{$key};
685             my $dsc= "";
686             my $vals= "";
687             if (defined $descriptions->{$key}){
688               $dsc= $descriptions->{$key};
689             }
690             if (defined $values->{$key}){
691               $vals= $values->{$key};
692             }
693             $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
694             $item.= "</item>";
695             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
696         }
697     }
699     $xml_msg=~ s/<xxx><\/xxx>//;
701     # Return message
702     return ( $xml_msg );
706 ## @method opsi_set_product_properties
707 # 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.
708 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
709 # @param msg_hash - HASHREF - message information parsed into a hash
710 # @param session_id - INTEGER - POE session id of the processing of this message
711 # @return out_msg - STRING - feedback to GOsa in success and error case
712 sub opsi_set_product_properties {
713     my ($msg, $msg_hash, $session_id) = @_;
714     my $header = @{$msg_hash->{'header'}}[0];
715     my $source = @{$msg_hash->{'source'}}[0];
716     my $target = @{$msg_hash->{'target'}}[0];
717     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
718     my ($productId, $hostId);
720     # Build return message with twisted target and source
721     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
722     if (defined $forward_to_gosa) {
723         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
724     }
726     # Sanity check of needed parameter
727     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
728         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
729         &add_content2xml_hash($out_hash, "error", "productId");
730         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
731         return ( &create_xml_string($out_hash) );
732     }
733     if (not exists $msg_hash->{'item'}) {
734         &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
735         &add_content2xml_hash($out_hash, "error", "item");
736         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
737         return ( &create_xml_string($out_hash) );
738     } else {
739         if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
740             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
741             &add_content2xml_hash($out_hash, "error", "name");
742             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
743             return ( &create_xml_string($out_hash) );
744         }
745         if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
746             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
747             &add_content2xml_hash($out_hash, "error", "value");
748             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
749             return ( &create_xml_string($out_hash) );
750         }
751     }
752     # if no hostId is given, set_product_properties will act on globally
753     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
754         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
755         &add_content2xml_hash($out_hash, "error", "hostId");
756         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
757         return ( &create_xml_string($out_hash) );
758     }
760         
761     # Get productId
762     $productId =  @{$msg_hash->{'productId'}}[0];
763     &add_content2xml_hash($out_hash, "productId", $productId);
765     # Get hostId if defined
766     if (exists $msg_hash->{'hostId'}){
767         $hostId = @{$msg_hash->{'hostId'}}[0];
768         &add_content2xml_hash($out_hash, "hostId", $hostId);
769     }
771     # Set product states if requested
772     if (defined @{$msg_hash->{'action'}}[0]){
773         &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
774     }
775     if (defined @{$msg_hash->{'state'}}[0]){
776         &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
777     }
779     # Find properties
780     foreach my $item (@{$msg_hash->{'item'}}){
781         # JSON Query
782         my $callobj;
784         if (defined $hostId){
785             $callobj = {
786                 method  => 'setProductProperty',
787                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
788                 id  => 1,
789             };
790         } else {
791             $callobj = {
792                 method  => 'setProductProperty',
793                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
794                 id  => 1,
795             };
796         }
798         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
799         my ($res_err, $res_err_string) = &check_opsi_res($res);
801         if ($res_err){
802             &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
803             &add_content2xml_hash($out_hash, "error", $res_err_string);
804         }
805     }
808     # Return message
809     return ( &create_xml_string($out_hash) );
813 ## @method opsi_get_client_hardware
814 # Reports client hardware inventory.
815 # @param msg - STRING - xml message with tag hostId
816 # @param msg_hash - HASHREF - message information parsed into a hash
817 # @param session_id - INTEGER - POE session id of the processing of this message
818 # @return out_msg - STRING - feedback to GOsa in success and error case
819 sub opsi_get_client_hardware {
820     my ($msg, $msg_hash, $session_id) = @_;
821     my $header = @{$msg_hash->{'header'}}[0];
822     my $source = @{$msg_hash->{'source'}}[0];
823     my $target = @{$msg_hash->{'target'}}[0];
824     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
825     my $hostId;
826     my $error = 0;
827     my $xml_msg;
829     # Build return message with twisted target and source
830     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
831     if (defined $forward_to_gosa) {
832       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
833     }
835     # Sanity check of needed parameter
836     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
837         $error++;
838         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
839         &add_content2xml_hash($out_hash, "error", "hostId");
840         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
841     }
843     if (not $error) {
845     # Get hostId
846         $hostId = @{$msg_hash->{'hostId'}}[0];
847         &add_content2xml_hash($out_hash, "hostId", "$hostId");
848         &add_content2xml_hash($out_hash, "xxx", "");
849     }    
851     # Move to XML string
852     $xml_msg= &create_xml_string($out_hash);
853     
854     if (not $error) {
856     # JSON Query
857         my $callobj = {
858             method  => 'getHardwareInformation_hash',
859             params  => [ $hostId ],
860             id  => 1,
861         };
863         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
864         if (not &check_opsi_res($res)){
865             my $result= $res->result;
866             if (ref $result eq "HASH") {
867                 foreach my $r (keys %{$result}){
868                     my $item= "\n<item><id>".xml_quote($r)."</id>";
869                     my $value= $result->{$r};
870                     foreach my $sres (@{$value}){
872                         foreach my $dres (keys %{$sres}){
873                             if (defined $sres->{$dres}){
874                                 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
875                             }
876                         }
878                     }
879                     $item.= "</item>";
880                     $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
882                 }
883             }
884         }
886         $xml_msg=~ s/<xxx><\/xxx>//;
888     }
890     # Return message
891     return ( $xml_msg );
895 ## @method opsi_list_clients
896 # Reports all Opsi clients. 
897 # @param msg - STRING - xml message 
898 # @param msg_hash - HASHREF - message information parsed into a hash
899 # @param session_id - INTEGER - POE session id of the processing of this message
900 # @return out_msg - STRING - feedback to GOsa in success and error case
901 sub opsi_list_clients {
902     my ($msg, $msg_hash, $session_id) = @_;
903     my $header = @{$msg_hash->{'header'}}[0];
904     my $source = @{$msg_hash->{'source'}}[0];
905     my $target = @{$msg_hash->{'target'}}[0];
906     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
908     # Build return message with twisted target and source
909     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
910     if (defined $forward_to_gosa) {
911       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
912     }
913     &add_content2xml_hash($out_hash, "xxx", "");
915     # Move to XML string
916     my $xml_msg= &create_xml_string($out_hash);
918     # JSON Query
919     my $callobj = {
920         method  => 'getClients_listOfHashes',
921         params  => [ ],
922         id  => 1,
923     };
924     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
925     if (not &check_opsi_res($res)){
926         foreach my $host (@{$res->result}){
927             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
928             if (defined($host->{'description'})){
929                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
930             }
931             if (defined($host->{'notes'})){
932                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
933             }
934             if (defined($host->{'lastSeen'})){
935                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
936             }
938             $callobj = {
939               method  => 'getIpAddress',
940               params  => [ $host->{'hostId'} ],
941               id  => 1,
942             };
943             my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
944             if ( not &check_opsi_res($sres)){
945               $item.= "<ip>".xml_quote($sres->result)."</ip>";
946             }
948             $callobj = {
949               method  => 'getMacAddress',
950               params  => [ $host->{'hostId'} ],
951               id  => 1,
952             };
953             $sres= $main::opsi_client->call($main::opsi_url, $callobj);
954             if ( not &check_opsi_res($sres)){
955                 $item.= "<mac>".xml_quote($sres->result)."</mac>";
956             }
957             $item.= "</item>";
958             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
959         }
960     }
962     $xml_msg=~ s/<xxx><\/xxx>//;
963     return ( $xml_msg );
968 ## @method opsi_get_client_software
969 # Reports client software inventory.
970 # @param msg - STRING - xml message with tag hostId
971 # @param msg_hash - HASHREF - message information parsed into a hash
972 # @param session_id - INTEGER - POE session id of the processing of this message
973 # @return out_msg - STRING - feedback to GOsa in success and error case
974 sub opsi_get_client_software {
975     my ($msg, $msg_hash, $session_id) = @_;
976     my $header = @{$msg_hash->{'header'}}[0];
977     my $source = @{$msg_hash->{'source'}}[0];
978     my $target = @{$msg_hash->{'target'}}[0];
979     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
980     my $error = 0;
981     my $hostId;
982     my $xml_msg;
984     # Build return message with twisted target and source
985     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
986     if (defined $forward_to_gosa) {
987       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
988     }
990     # Sanity check of needed parameter
991     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
992         $error++;
993         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
994         &add_content2xml_hash($out_hash, "error", "hostId");
995         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
996     }
998     if (not $error) {
1000     # Get hostId
1001         $hostId = @{$msg_hash->{'hostId'}}[0];
1002         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1003         &add_content2xml_hash($out_hash, "xxx", "");
1004     }
1006     $xml_msg= &create_xml_string($out_hash);
1008     if (not $error) {
1010     # JSON Query
1011         my $callobj = {
1012             method  => 'getSoftwareInformation_hash',
1013             params  => [ $hostId ],
1014             id  => 1,
1015         };
1017         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1018         if (not &check_opsi_res($res)){
1019             my $result= $res->result;
1020         }
1022         $xml_msg=~ s/<xxx><\/xxx>//;
1024     }
1026     # Return message
1027     return ( $xml_msg );
1031 ## @method opsi_get_local_products
1032 # Reports product for given hostId or globally.
1033 # @param msg - STRING - xml message with optional tag hostId
1034 # @param msg_hash - HASHREF - message information parsed into a hash
1035 # @param session_id - INTEGER - POE session id of the processing of this message
1036 # @return out_msg - STRING - feedback to GOsa in success and error case
1037 sub opsi_get_local_products {
1038     my ($msg, $msg_hash, $session_id) = @_;
1039     my $header = @{$msg_hash->{'header'}}[0];
1040     my $source = @{$msg_hash->{'source'}}[0];
1041     my $target = @{$msg_hash->{'target'}}[0];
1042     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1043     my $hostId;
1045     # Build return message with twisted target and source
1046     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1047     if (defined $forward_to_gosa) {
1048         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1049     }
1050     &add_content2xml_hash($out_hash, "xxx", "");
1052     # Get hostId if defined
1053     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
1054         $hostId = @{$msg_hash->{'hostId'}}[0];
1055         &add_content2xml_hash($out_hash, "hostId", $hostId);
1056     }
1058     # Move to XML string
1059     my $xml_msg= &create_xml_string($out_hash);
1061     # For hosts, only return the products that are or get installed
1062     my $callobj;
1063     $callobj = {
1064         method  => 'getLocalBootProductIds_list',
1065         params  => [ ],
1066         id  => 1,
1067     };
1069     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1070     my %r = ();
1071     for (@{$res->result}) { $r{$_} = 1 }
1073     if (not &check_opsi_res($res)){
1075         if (defined $hostId){
1076             $callobj = {
1077                 method  => 'getProductStates_hash',
1078                 params  => [ $hostId ],
1079                 id  => 1,
1080             };
1082             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1083             if (not &check_opsi_res($hres)){
1084                 my $htmp= $hres->result->{$hostId};
1086                 # Check state != not_installed or action == setup -> load and add
1087                 foreach my $product (@{$htmp}){
1089                     if (!defined ($r{$product->{'productId'}})){
1090                         next;
1091                     }
1093                     # Now we've a couple of hashes...
1094                     if ($product->{'installationStatus'} ne "not_installed" or
1095                             $product->{'actionRequest'} eq "setup"){
1096                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1098                         $callobj = {
1099                             method  => 'getProduct_hash',
1100                             params  => [ $product->{'productId'} ],
1101                             id  => 1,
1102                         };
1104                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1105                         if (not &check_opsi_res($sres)){
1106                             my $tres= $sres->result;
1108                             my $name= xml_quote($tres->{'name'});
1109                             my $r= $product->{'productId'};
1110                             my $description= xml_quote($tres->{'description'});
1111                             $name=~ s/\//\\\//;
1112                             $description=~ s/\//\\\//;
1113                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1114                         }
1116                     }
1117                 }
1119             }
1121         } else {
1122             foreach my $r (@{$res->result}) {
1123                 $callobj = {
1124                     method  => 'getProduct_hash',
1125                     params  => [ $r ],
1126                     id  => 1,
1127                 };
1129                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1130                 if (not &check_opsi_res($sres)){
1131                     my $tres= $sres->result;
1133                     my $name= xml_quote($tres->{'name'});
1134                     my $description= xml_quote($tres->{'description'});
1135                     $name=~ s/\//\\\//;
1136                     $description=~ s/\//\\\//;
1137                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1138                 }
1140             }
1142         }
1143     }
1145     $xml_msg=~ s/<xxx><\/xxx>//;
1147     # Retrun Message
1148     return ( $xml_msg );
1152 ## @method opsi_del_client
1153 # Deletes a client from Opsi.
1154 # @param msg - STRING - xml message with tag hostId
1155 # @param msg_hash - HASHREF - message information parsed into a hash
1156 # @param session_id - INTEGER - POE session id of the processing of this message
1157 # @return out_msg - STRING - feedback to GOsa in success and error case
1158 sub opsi_del_client {
1159     my ($msg, $msg_hash, $session_id) = @_;
1160     my $header = @{$msg_hash->{'header'}}[0];
1161     my $source = @{$msg_hash->{'source'}}[0];
1162     my $target = @{$msg_hash->{'target'}}[0];
1163     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1164     my $hostId;
1165     my $error = 0;
1167     # Build return message with twisted target and source
1168     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1169     if (defined $forward_to_gosa) {
1170       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1171     }
1173     # Sanity check of needed parameter
1174     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1175         $error++;
1176         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1177         &add_content2xml_hash($out_hash, "error", "hostId");
1178         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1179     }
1181     if (not $error) {
1183     # Get hostId
1184         $hostId = @{$msg_hash->{'hostId'}}[0];
1185         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1187     # JSON Query
1188         my $callobj = {
1189             method  => 'deleteClient',
1190             params  => [ $hostId ],
1191             id  => 1,
1192         };
1193         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1194     }
1196     # Move to XML string
1197     my $xml_msg= &create_xml_string($out_hash);
1199     # Return message
1200     return ( $xml_msg );
1204 ## @method opsi_install_client
1205 # Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1206 # @param msg - STRING - xml message with tags hostId, macaddress
1207 # @param msg_hash - HASHREF - message information parsed into a hash
1208 # @param session_id - INTEGER - POE session id of the processing of this message
1209 # @return out_msg - STRING - feedback to GOsa in success and error case
1210 sub opsi_install_client {
1211     my ($msg, $msg_hash, $session_id) = @_;
1212     my $header = @{$msg_hash->{'header'}}[0];
1213     my $source = @{$msg_hash->{'source'}}[0];
1214     my $target = @{$msg_hash->{'target'}}[0];
1215     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1218     my ($hostId, $macaddress);
1220     my $error = 0;
1221     my @out_msg_l;
1223     # Build return message with twisted target and source
1224     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1225     if (defined $forward_to_gosa) {
1226         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1227     }
1229     # Sanity check of needed parameter
1230     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1231         $error++;
1232         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1233         &add_content2xml_hash($out_hash, "error", "hostId");
1234         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1235     }
1236     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
1237         $error++;
1238         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1239         &add_content2xml_hash($out_hash, "error", "macaddress");
1240         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1241     } else {
1242         if ((exists $msg_hash->{'macaddress'}) && 
1243                 ($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)) {  
1244             $macaddress = $1; 
1245         } else { 
1246             $error ++; 
1247             &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1248             &add_content2xml_hash($out_hash, "error", "macaddress");
1249             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1250         }
1251     }
1253     if (not $error) {
1255     # Get hostId
1256         $hostId = @{$msg_hash->{'hostId'}}[0];
1257         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1259         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1260         my $callobj = {
1261             method  => 'getProductStates_hash',
1262             params  => [ $hostId ],
1263             id  => 1,
1264         };
1266         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1267         if (not &check_opsi_res($hres)){
1268             my $htmp= $hres->result->{$hostId};
1270             # check state != not_installed or action == setup -> load and add
1271             foreach my $product (@{$htmp}){
1272                 # Now we've a couple of hashes...
1273                 if ($product->{'installationStatus'} ne "not_installed" or
1274                         $product->{'actionRequest'} ne "none"){
1276                     # Do an action request for all these -> "setup".
1277                     $callobj = {
1278                         method  => 'setProductActionRequest',
1279                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1280                         id  => 1,
1281                     };
1282                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1283                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1284                     if ($res_err){
1285                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1286                     } else {
1287                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1288                     }
1289                 }
1290             }
1291         }
1292         push(@out_msg_l, &create_xml_string($out_hash));
1293     
1295     # Build wakeup message for client
1296         if (not $error) {
1297             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1298             &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1299             my $wakeup_msg = &create_xml_string($wakeup_hash);
1300             push(@out_msg_l, $wakeup_msg);
1302             # invoke trigger wake for this gosa-si-server
1303             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1304         }
1305     }
1306     
1307     # Return messages
1308     return @out_msg_l;
1312 ## @method _set_action
1313 # Set action for an Opsi client
1314 # @param product - STRING - Opsi product
1315 # @param action - STRING - action
1316 # @param hostId - STRING - Opsi hostId
1317 sub _set_action {
1318   my $product= shift;
1319   my $action = shift;
1320   my $hostId = shift;
1321   my $callobj;
1323   $callobj = {
1324     method  => 'setProductActionRequest',
1325     params  => [ $product, $hostId, $action],
1326     id  => 1,
1327   };
1329   $main::opsi_client->call($main::opsi_url, $callobj);
1332 ## @method _set_state
1333 # Set state for an Opsi client
1334 # @param product - STRING - Opsi product
1335 # @param action - STRING - state
1336 # @param hostId - STRING - Opsi hostId
1337 sub _set_state {
1338   my $product = shift;
1339   my $state = shift;
1340   my $hostId = shift;
1341   my $callobj;
1343   $callobj = {
1344     method  => 'setProductState',
1345     params  => [ $product, $hostId, $state ],
1346     id  => 1,
1347   };
1349   $main::opsi_client->call($main::opsi_url, $callobj);
1352 ################################
1354 # @brief Create a license pool at Opsi server.
1355 # @param licensePoolId The name of the pool (optional). 
1356 # @param description The description of the pool (optional).
1357 # @param productIds A list of assigned porducts of the pool (optional). 
1358 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional). 
1360 sub opsi_createLicensePool {
1361     my ($msg, $msg_hash, $session_id) = @_;
1362     my $header = @{$msg_hash->{'header'}}[0];
1363     my $source = @{$msg_hash->{'source'}}[0];
1364     my $target = @{$msg_hash->{'target'}}[0];
1365         my $out_hash;
1366         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1367         my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1368         my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1369         my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1371         # Create license Pool
1372     my $callobj = {
1373         method  => 'createLicensePool',
1374         params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1375         id  => 1,
1376     };
1377     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1379         # Check Opsi error
1380         my ($res_error, $res_error_str) = &check_opsi_res($res);
1381         if ($res_error){
1382                 # Create error message
1383                 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1384                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1385                 return ( &create_xml_string($out_hash) );
1386         }
1388         # Create function result message
1389         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1391         return ( &create_xml_string($out_hash) );
1394 ################################
1396 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1398 sub opsi_getLicensePools_listOfHashes {
1399     my ($msg, $msg_hash, $session_id) = @_;
1400     my $header = @{$msg_hash->{'header'}}[0];
1401     my $source = @{$msg_hash->{'source'}}[0];
1402         my $out_hash;
1404         # Fetch infos from Opsi server
1405     my $callobj = {
1406         method  => 'getLicensePools_listOfHashes',
1407         params  => [ ],
1408         id  => 1,
1409     };
1410     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1412         # Check Opsi error
1413         my ($res_error, $res_error_str) = &check_opsi_res($res);
1414         if ($res_error){
1415                 # Create error message
1416                 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1417                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1418                 return ( &create_xml_string($out_hash) );
1419         }
1421         # Create function result message
1422         my $res_hash = { 'hit'=> [] };
1423         foreach my $licensePool ( @{$res->result}) {
1424                 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1425                         'description' => [$licensePool->{'description'}],
1426                         'productIds' => $licensePool->{'productIds'},
1427                         'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1428                         };
1429                 push( @{$res_hash->{hit}}, $licensePool_hash );
1430         }
1432         # Create function result message
1433         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1434         $out_hash->{result} = [$res_hash];
1436         return ( &create_xml_string($out_hash) );
1439 ################################
1441 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1442 # @param licensePoolId The name of the pool. 
1444 sub opsi_getLicensePool_hash {
1445     my ($msg, $msg_hash, $session_id) = @_;
1446     my $header = @{$msg_hash->{'header'}}[0];
1447     my $source = @{$msg_hash->{'source'}}[0];
1448     my $target = @{$msg_hash->{'target'}}[0];
1449     my $licensePoolId;
1450         my $out_hash;
1452         # Check input sanity
1453         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1454                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1455         } else {
1456                 return ( &_giveErrorFeedback($msg_hash, "", $session_id, $_) );
1457         }
1459         # Fetch infos from Opsi server
1460     my $callobj = {
1461         method  => 'getLicensePool_hash',
1462         params  => [ $licensePoolId ],
1463         id  => 1,
1464     };
1465     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1467         # Check Opsi error
1468         my ($res_error, $res_error_str) = &check_opsi_res($res);
1469         if ($res_error){
1470                 # Create error message
1471                 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1472                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1473                 &add_content2xml_hash($out_hash, "error", $res_error_str);
1474                 return ( &create_xml_string($out_hash) );
1475         }
1477         # Create function result message
1478         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1479         &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1480         &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1481         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1482         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1484         return ( &create_xml_string($out_hash) );
1487 sub _parse_getSoftwareLicenseUsages {
1488         my $res = shift;
1490         # Parse Opsi result
1491         my $tmp_licensePool_cache = {};
1492         my $res_hash = { 'hit'=> [] };
1493         foreach my $license ( @{$res}) {
1494                 my $tmp_licensePool = $license->{'licensePoolId'};
1495                 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1496                         # Fetch missing informations from Opsi and cache the results for a possible later usage
1497                         my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1498                         if (not $err) {
1499                                 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1500                         }
1501                 }
1502                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1503                         'notes' => [$license->{'notes'}],
1504                         'licenseKey' => [$license->{'licenseKey'}],
1505                         'hostId' => [$license->{'hostId'}],
1506                         'licensePoolId' => [$tmp_licensePool],
1507                         };
1508                 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1509                         $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1510                         map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1511                         map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1512                 }
1513                 push( @{$res_hash->{hit}}, $license_hash );
1514         }
1516         return $res_hash;
1519 ################################
1521 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1522 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1523 # @param licensePoolId The name of the pool (optional). 
1524
1525 sub opsi_getSoftwareLicenseUsages {
1526         my ($msg, $msg_hash, $session_id) = @_;
1527         my $header = @{$msg_hash->{'header'}}[0];
1528         my $source = @{$msg_hash->{'source'}}[0];
1529         my $target = @{$msg_hash->{'target'}}[0];
1530         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1531         my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1532         my $out_hash;
1534         my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1535         if ($err){
1536                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1537         }
1539         # Parse Opsi result
1540         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1542         # Create function result message
1543         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1544         $out_hash->{result} = [$res_hash];
1546         return ( &create_xml_string($out_hash) );
1549 ################################
1551 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1552 # @param productId Something like 'firefox', 'python' or anything else .
1553
1554 sub opsi_getSoftwareLicenseUsagesForProductId {
1555         my ($msg, $msg_hash, $session_id) = @_;
1556         my $header = @{$msg_hash->{'header'}}[0];
1557         my $source = @{$msg_hash->{'source'}}[0];
1559         # Check input sanity
1560         my $productId;
1561         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1562                 $productId= @{$msg_hash->{'productId'}}[0];
1563         } else {
1564                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1565         }
1567         # Fetch licensePoolId for productId
1568         my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1569         if ($err){
1570                 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1571         }
1573         my $licensePoolId;
1575         # Fetch softwareLiceceUsages for licensePoolId
1576         ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1577         if ($err){
1578                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1579         }
1581         # Parse Opsi result
1582         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1584         # Create function result message
1585         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1586         $out_hash->{result} = [$res_hash];
1588         return ( &create_xml_string($out_hash) );
1591 ################################
1593 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1594 # @param softwareLicenseId Identificator of a license.
1596 sub opsi_getSoftwareLicense_hash {
1597         my ($msg, $msg_hash, $session_id) = @_;
1598         my $header = @{$msg_hash->{'header'}}[0];
1599         my $source = @{$msg_hash->{'source'}}[0];
1600         my $target = @{$msg_hash->{'target'}}[0];
1601         my $softwareLicenseId;
1602         my $out_hash;
1604         # Check input sanity
1605         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1606                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1607         } else {
1608                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1609         }
1611         my $callobj = {
1612                 method  => 'getSoftwareLicense_hash',
1613                 params  => [ $softwareLicenseId ],
1614                 id  => 1,
1615         };
1616         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1618         # Check Opsi error
1619         my ($res_error, $res_error_str) = &check_opsi_res($res);
1620         if ($res_error){
1621                 # Create error message
1622                 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1623                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1624                 return ( &create_xml_string($out_hash) );
1625         }
1626         
1627         # Create function result message
1628         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1629         &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1630         &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1631         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1632         &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1633         foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1634                 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1635                 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1636         }
1638         return ( &create_xml_string($out_hash) );
1641 ################################
1643 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
1644 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted. 
1645 # @param licensePoolId The name of the pool. 
1647 sub opsi_deleteLicensePool {
1648         my ($msg, $msg_hash, $session_id) = @_;
1649     my $header = @{$msg_hash->{'header'}}[0];
1650     my $source = @{$msg_hash->{'source'}}[0];
1651     my $target = @{$msg_hash->{'target'}}[0];
1652     my $licensePoolId;
1653         my $out_hash;
1655         # Check input sanity
1656         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1657                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1658         } else {
1659                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1660         }
1662         # Fetch softwareLicenseIds used in license pool
1663         # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1664         # but not the license contracts of the software licenses. In our case each software license has exactly one license contract. 
1665         my $callobj = {
1666                 method  => 'getSoftwareLicenses_listOfHashes',
1667                 params  => [ ],
1668                 id  => 1,
1669         };
1670         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1672         # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1673         my @lCI_toBeDeleted;
1674         foreach my $softwareLicenseHash ( @{$res->result} ) {
1675                 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) { 
1676                         next; 
1677                 }  
1678                 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1679         }
1681         # Delete license pool at Opsi server
1682     $callobj = {
1683         method  => 'deleteLicensePool',
1684         params  => [ $licensePoolId, 'deleteLicenses=True'  ],
1685         id  => 1,
1686     };
1687     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1688         my ($res_error, $res_error_str) = &check_opsi_res($res);
1689         if ($res_error){
1690                 # Create error message
1691                 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1692                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1693                 return ( &create_xml_string($out_hash) );
1694         } 
1696         # Delete each license contract connected with the license pool
1697         foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1698                 my $callobj = {
1699                         method  => 'deleteLicenseContract',
1700                         params  => [ $licenseContractId ],
1701                         id  => 1,
1702                 };
1703                 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1704                 my ($res_error, $res_error_str) = &check_opsi_res($res);
1705                 if ($res_error){
1706                         # Create error message
1707                         &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1708                         $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1709                         return ( &create_xml_string($out_hash) );
1710                 }
1711         }
1713         # Create function result message
1714         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1715         
1716         return ( &create_xml_string($out_hash) );
1719 ################################
1721 # @brief Create a license contract, create a software license and add the software license to the license pool
1722 # @param licensePoolId The name of the pool the license should be assigned.
1723 # @param licenseKey The license key.
1724 # @param partner Name of the license partner (optional).
1725 # @param conclusionDate Date of conclusion of license contract (optional)
1726 # @param notificationDate Date of notification that license is running out soon (optional).
1727 # @param notes This is the place for some notes (optional)
1728 # @param softwareLicenseId Identificator of a license (optional).
1729 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1730 # @param maxInstallations The number of clients use this license (optional). 
1731 # @param boundToHost The name of the client the license is bound to (optional).
1732 # @param expirationDate The date when the license is running down (optional). 
1734 sub opsi_createLicense {
1735         my ($msg, $msg_hash, $session_id) = @_;
1736     my $header = @{$msg_hash->{'header'}}[0];
1737     my $source = @{$msg_hash->{'source'}}[0];
1738     my $target = @{$msg_hash->{'target'}}[0];
1739         my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1740         my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1741         my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1742         my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1743         my $licenseContractId = undef;
1744         my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1745         my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1746         my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1747         my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1748         my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1749         my $licensePoolId;
1750         my $licenseKey;
1751         my $out_hash;
1753         # Check input sanity
1754         if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1755                 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1756         } else {
1757                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1758         }
1759         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1760                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1761         } else {
1762                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1763         }
1764         if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1765                 return ( &_give_feedback($msg, $msg_hash, $session_id, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'."));
1766         }
1767         
1768         # Automatically define licenseContractId if ID is not given
1769         if (defined $softwareLicenseId) { 
1770                 $licenseContractId = "c_".$softwareLicenseId;
1771         }
1773         # Create license contract at Opsi server
1774     my $callobj = {
1775         method  => 'createLicenseContract',
1776         params  => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1777         id  => 1,
1778     };
1779     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1781         # Check Opsi error
1782         my ($res_error, $res_error_str) = &check_opsi_res($res);
1783         if ($res_error){
1784                 # Create error message
1785                 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1786                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1787                 return ( &create_xml_string($out_hash) );
1788         }
1789         
1790         $licenseContractId = $res->result;
1792         # Create software license at Opsi server
1793     $callobj = {
1794         method  => 'createSoftwareLicense',
1795         params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1796         id  => 1,
1797     };
1798     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1800         # Check Opsi error
1801         ($res_error, $res_error_str) = &check_opsi_res($res);
1802         if ($res_error){
1803                 # Create error message
1804                 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1805                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1806                 return ( &create_xml_string($out_hash) );
1807         }
1809         $softwareLicenseId = $res->result;
1811         # Add software license to license pool
1812         $callobj = {
1813         method  => 'addSoftwareLicenseToLicensePool',
1814         params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1815         id  => 1,
1816     };
1817     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1819         # Check Opsi error
1820         ($res_error, $res_error_str) = &check_opsi_res($res);
1821         if ($res_error){
1822                 # Create error message
1823                 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1824                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1825                 return ( &create_xml_string($out_hash) );
1826         }
1828         # Create function result message
1829         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1830         
1831         return ( &create_xml_string($out_hash) );
1834 ################################
1836 # @brief Assign a software license to a host
1837 # @param hostid Something like client_1.intranet.mydomain.de
1838 # @param licensePoolId The name of the pool.
1840 sub opsi_assignSoftwareLicenseToHost {
1841         my ($msg, $msg_hash, $session_id) = @_;
1842     my $header = @{$msg_hash->{'header'}}[0];
1843     my $source = @{$msg_hash->{'source'}}[0];
1844     my $target = @{$msg_hash->{'target'}}[0];
1845         my $hostId;
1846         my $licensePoolId;
1848         # Check input sanity
1849         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1850                 $hostId = @{$msg_hash->{'hostId'}}[0];
1851         } else {
1852                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1853         }
1854         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1855                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1856         } else {
1857                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1858         }
1860         # Assign a software license to a host
1861         my $callobj = {
1862         method  => 'getAndAssignSoftwareLicenseKey',
1863         params  => [ $hostId, $licensePoolId ],
1864         id  => 1,
1865     };
1866     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1868         # Check Opsi error
1869         my ($res_error, $res_error_str) = &check_opsi_res($res);
1870         if ($res_error){
1871                 # Create error message
1872                 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1873                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1874                 return ( &create_xml_string($out_hash) );
1875         }
1877         # Create function result message
1878         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1879         
1880         return ( &create_xml_string($out_hash) );
1883 ################################
1885 # @brief Unassign a software license from a host.
1886 # @param hostid Something like client_1.intranet.mydomain.de
1887 # @param licensePoolId The name of the pool.
1889 sub opsi_unassignSoftwareLicenseFromHost {
1890         my ($msg, $msg_hash, $session_id) = @_;
1891     my $header = @{$msg_hash->{'header'}}[0];
1892     my $source = @{$msg_hash->{'source'}}[0];
1893     my $target = @{$msg_hash->{'target'}}[0];
1894         my $hostId;
1895         my $licensePoolId;
1897         # Check input sanity
1898         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1899                 $hostId = @{$msg_hash->{'hostId'}}[0];
1900         } else {
1901                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1902         }
1903         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1904                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1905         } else {
1906                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1907         }
1909         # Unassign a software license from a host
1910         my $callobj = {
1911         method  => 'deleteSoftwareLicenseUsage',
1912         params  => [ $hostId, '', $licensePoolId ],
1913         id  => 1,
1914     };
1915     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1917         # Check Opsi error
1918         my ($res_error, $res_error_str) = &check_opsi_res($res);
1919         if ($res_error){
1920                 # Create error message
1921                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1922                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1923                 return ( &create_xml_string($out_hash) );
1924         }
1926         # Create function result message
1927         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1928         
1929         return ( &create_xml_string($out_hash) );
1932 ################################
1934 # @brief Unassign all software licenses from a host
1935 # @param hostid Something like client_1.intranet.mydomain.de
1937 sub opsi_unassignAllSoftwareLicensesFromHost {
1938         my ($msg, $msg_hash, $session_id) = @_;
1939     my $header = @{$msg_hash->{'header'}}[0];
1940     my $source = @{$msg_hash->{'source'}}[0];
1941     my $target = @{$msg_hash->{'target'}}[0];
1942         my $hostId;
1944         # Check input sanity
1945         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1946                 $hostId = @{$msg_hash->{'hostId'}}[0];
1947         } else {
1948                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1949         }
1951         # Unassign all software licenses from a host
1952         my $callobj = {
1953         method  => 'deleteAllSoftwareLicenseUsages',
1954         params  => [ $hostId ],
1955         id  => 1,
1956     };
1957     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1959         # Check Opsi error
1960         my ($res_error, $res_error_str) = &check_opsi_res($res);
1961         if ($res_error){
1962                 # Create error message
1963                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1964                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1965                 return ( &create_xml_string($out_hash) );
1966         }
1968         # Create function result message
1969         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1970         
1971         return ( &create_xml_string($out_hash) );
1975 ################################
1977 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1978 # and the number of max and remaining installations for a given OPSI product.
1979 # @param productId Identificator of an OPSI product.
1980 #       
1981 sub opsi_getLicenseInformationForProduct {
1982     my ($msg, $msg_hash, $session_id) = @_;
1983     my $header = @{$msg_hash->{'header'}}[0];
1984     my $source = @{$msg_hash->{'source'}}[0];
1985         my $productId;
1986         my $out_hash;
1988         # Check input sanity
1989         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1990                 $productId = @{$msg_hash->{'productId'}}[0];
1991         } else {
1992                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1993         }
1995         # Fetch infos from Opsi server
1996     my $callobj = {
1997         method  => 'getLicensePoolId',
1998         params  => [ $productId ],
1999         id  => 1,
2000     };
2001     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2003         # Check Opsi error
2004         my ($res_error, $res_error_str) = &check_opsi_res($res);
2005         if ($res_error){
2006                 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
2007         } 
2008         
2009         my $licensePoolId = $res->result;
2011         # Fetch statistic information for given pool ID
2012         $callobj = {
2013                 method  => 'getLicenseStatistics_hash',
2014                 params  => [ ],
2015                 id  => 1,
2016         };
2017         $res = $main::opsi_client->call($main::opsi_url, $callobj);
2019         # Check Opsi error
2020         ($res_error, $res_error_str) = &check_opsi_res($res);
2021         if ($res_error){
2022                 # Create error message
2023                 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
2024                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
2025                 return ( &create_xml_string($out_hash) );
2026         }
2028         # Create function result message
2029         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2030         &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
2031         &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
2032         &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
2033         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
2034         &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
2035         map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
2037         return ( &create_xml_string($out_hash) );
2041 ################################
2043 # @brief
2044 # @param 
2045 #       
2046 sub opsi_getPool {
2047     my ($msg, $msg_hash, $session_id) = @_;
2048     my $header = @{$msg_hash->{'header'}}[0];
2049     my $source = @{$msg_hash->{'source'}}[0];
2051         # Check input sanity
2052         my $licensePoolId;
2053         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2054                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2055         } else {
2056                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2057         }
2059         # Create hash for the answer
2060         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2062         # Call Opsi
2063         my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2064         if ($err){
2065                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2066         }
2067         # Add data to outgoing hash
2068         &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2069         &add_content2xml_hash($out_hash, "description", $res->{'description'});
2070         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2071         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2074         # Call Opsi two times
2075         my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2076         if ($usages_err){
2077                 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
2078         }
2079         my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2080         if ($licenses_err){
2081                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2082         }
2084         # Add data to outgoing hash
2085         # Parse through all software licenses and select those associated to the pool
2086         my $res_hash = { 'hit'=> [] };
2087         foreach my $license ( @$licenses_res) {
2088                 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2089                 my $found = 0;
2090                 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2091                 foreach my $lPI ( @licensePoolIds_list) {
2092                         if ($lPI eq $licensePoolId) { $found++ }
2093                 }
2094                 if (not $found ) { next; };
2095                 # Found matching licensePoolId
2096                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2097                         'licenseKeys' => {},
2098                         'expirationDate' => [$license->{'expirationDate'}],
2099                         'boundToHost' => [$license->{'boundToHost'}],
2100                         'maxInstallations' => [$license->{'maxInstallations'}],
2101                         'licenseType' => [$license->{'licenseType'}],
2102                         'licenseContractId' => [$license->{'licenseContractId'}],
2103                         'licensePoolIds' => [],
2104                         'hostIds' => [],
2105                         };
2106                 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2107                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2108                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2109                 }
2110                 foreach my $usage (@$usages_res) {
2111                         # Search for hostIds with matching softwareLicenseId
2112                         if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2113                                 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2114                         }
2115                 }
2117                 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
2118                 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
2119                 if ($lContract_err){
2120                         return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
2121                 }
2122                 $license_hash->{$license->{'licenseContractId'}} = [];
2123                 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
2124                         'notificationDate' => [$lContract_res->{notificationDate}],
2125                         'notes' => [$lContract_res->{notes}],
2126                         'exirationDate' => [$lContract_res->{expirationDate}],
2127                         'partner' => [$lContract_res->{partner}],
2128                 };
2129                 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2131                 push( @{$res_hash->{hit}}, $license_hash );
2132         }
2133         $out_hash->{licenses} = [$res_hash];
2135     return ( &create_xml_string($out_hash) );
2139 ################################
2141 # @brief Removes at first the software license from license pool and than deletes the software license. 
2142 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2143 # @param softwareLicenseId 
2144 # @param licensePoolId
2146 sub opsi_removeLicense {
2147     my ($msg, $msg_hash, $session_id) = @_;
2148     my $header = @{$msg_hash->{'header'}}[0];
2149     my $source = @{$msg_hash->{'source'}}[0];
2151         # Check input sanity
2152         my $softwareLicenseId;
2153         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2154                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2155         } else {
2156                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2157         }
2158         my $licensePoolId;
2159         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2160                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2161         } else {
2162                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2163         }
2164         
2165         # Call Opsi
2166         my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2167         if ($err){
2168                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2169         }
2171         # Call Opsi
2172         ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2173         if ($err){
2174                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2175         }
2177         # Create hash for the answer
2178         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2179         return ( &create_xml_string($out_hash) );
2183 ################################
2185 # @brief
2186 # @param 
2188 sub opsi_getReservedLicenses {
2189         my ($msg, $msg_hash, $session_id) = @_;
2190         my $header = @{$msg_hash->{'header'}}[0];
2191         my $source = @{$msg_hash->{'source'}}[0];
2193         # Check input sanity
2194         my $hostId;
2195         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2196                 $hostId = @{$msg_hash->{'hostId'}}[0];
2197         } else {
2198                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2199         }
2201         # Fetch informations from Opsi server
2202         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2203         if ($license_err){
2204                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2205         }
2208         # Parse result
2209         my $res_hash = { 'hit'=> [] };
2210         foreach my $license ( @$license_res) {
2211                 if ($license->{boundToHost} ne $hostId) { next; }
2213                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2214                         'maxInstallations' => [$license->{'maxInstallations'}],
2215                         'boundToHost' => [$license->{'boundToHost'}],
2216                         'expirationDate' => [$license->{'expirationDate'}],
2217                         'licenseContractId' => [$license->{'licenseContractId'}],
2218                         'licenseType' => [$license->{'licenseType'}],
2219                         'licensePoolIds' => [],
2220                         };
2221                 
2222                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2223                         # Fetch information for license pools containing a software license which is bound to given host
2224                         my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2225                         if ($pool_err){
2226                                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2227                         }
2229                         # Add licensePool information to result hash
2230                         push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2231                         $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2232                         map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2233                         map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2234                 }
2235                 push( @{$res_hash->{hit}}, $license_hash );
2236         }
2237         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2238         $out_hash->{licenses} = [$res_hash];
2239     return ( &create_xml_string($out_hash) );
2241         return;
2244 ################################
2246 # @brief
2247 # @param 
2249 sub opsi_boundHostToLicense {
2250         my ($msg, $msg_hash, $session_id) = @_;
2251         my $header = @{$msg_hash->{'header'}}[0];
2252         my $source = @{$msg_hash->{'source'}}[0];
2254         # Check input sanity
2255         my $hostId;
2256         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2257                 $hostId = @{$msg_hash->{'hostId'}}[0];
2258         } else {
2259                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2260         }
2261         my $softwareLicenseId;
2262         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2263                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2264         } else {
2265                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2266         }
2268         # Fetch informations from Opsi server
2269         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2270         if ($license_err){
2271                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2272         }
2274         # Memorize parameter for given softwareLicenseId
2275         my $licenseContractId;
2276         my $licenseType;
2277         my $maxInstallations;
2278         my $boundToHost;
2279         my $expirationDate = "";
2280         my $found;
2281         foreach my $license (@$license_res) {
2282                 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2283                 $licenseContractId = $license->{licenseContractId};
2284                 $licenseType = $license->{licenseType};
2285                 $maxInstallations = $license->{maxInstallations};
2286                 $expirationDate = $license->{expirationDate};
2287                 $found++;
2288         }
2290         if (not $found) {
2291                 return ( &_give_feedback($msg, $msg_hash, $session_id, "no softwarelicenseId found with name '".$softwareLicenseId."'") );
2292         }
2294         # Set boundToHost option for a given software license
2295         my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId, 
2296                         'licenseContractId' => $licenseContractId, 
2297                         'licenseType' => $licenseType, 
2298                         'maxInstallations' => $maxInstallations, 
2299                         'boundToHost' => $hostId, 
2300                         'expirationDate' => $expirationDate);
2301         if ($bound_err) {
2302                 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2303         }
2305         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2306     return ( &create_xml_string($out_hash) );
2309 ################################
2311 # @brief
2312 # @param 
2314 sub opsi_unboundHostFromLicense {
2315         # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2316         # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2317         # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2318         my ($msg, $msg_hash, $session_id) = @_;
2319         my $header = @{$msg_hash->{'header'}}[0];
2320         my $source = @{$msg_hash->{'source'}}[0];
2322         # Check input sanity
2323         my $softwareLicenseId;
2324         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2325                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2326         } else {
2327                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2328         }
2329         
2330         # Memorize parameter witch are required for this procedure
2331         my $licenseContractId;
2332         my $licenseType;
2333         my $maxInstallations;
2334         my $expirationDate;
2335         my $partner;
2336         my $conclusionDate;
2337         my $notificationDate;
2338         my $notes;
2339         my $licensePoolId;
2340         my $licenseKey;
2342         # Fetch license informations from Opsi server
2343         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2344         if ($license_err){
2345                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2346         }
2347         my $found = 0;
2348         foreach my $license (@$license_res) {
2349                 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2350                 $licenseContractId = $license->{licenseContractId};
2351                 $licenseType = $license->{licenseType};
2352                 $maxInstallations = $license->{maxInstallations};
2353                 $expirationDate = $license->{expirationDate};
2354                 $licensePoolId = @{$license->{licensePoolIds}}[0];
2355                 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2356                 $found++;
2357         }
2358         
2359         # Fetch contract informations from Opsi server
2360         my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2361         if ($contract_err){
2362                 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2363         }
2364         $partner = $contract_res->{partner};
2365         $conclusionDate = $contract_res->{conclusionDate};
2366         $notificationDate = $contract_res->{notificationDate};
2367         $expirationDate = $contract_res->{expirationDate};
2368         $notes = $contract_res->{notes};
2370         # Delete software license
2371         my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2372         if ($err) {
2373                 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2374         }
2376         # Recreate software license without boundToHost
2377         ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate, 
2378                         'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2379         if ($err) {
2380                 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2381         }
2382         ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType, 
2383                         'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate       );
2384         if ($err) {
2385                 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2386         }
2387         ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2388         if ($err) {
2389                 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2390         }
2392         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2393     return ( &create_xml_string($out_hash) );
2396 sub opsi_test {
2397     my ($msg, $msg_hash, $session_id) = @_;
2398     my $header = @{$msg_hash->{'header'}}[0];
2399     my $source = @{$msg_hash->{'source'}}[0];
2400         my $pram1 = @{$msg_hash->{'productId'}}[0];
2402 print STDERR Dumper $pram1;
2404         # Fetch infos from Opsi server
2405     my $callobj = {
2406         method  => 'getLicensePoolId',
2407         params  => [ $pram1 ],
2408         id  => 1,
2409     };
2410     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2412         print STDERR Dumper $res;
2413         return ();
2416 sub _giveErrorFeedback {
2417         my ($msg_hash, $err_string, $session_id) = @_;
2418         &main::daemon_log("$session_id ERROR: $err_string", 1);
2419         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2420         return ( &create_xml_string($out_hash) );
2424 sub _getLicensePool_hash {
2425         my %arg = (
2426                 'licensePoolId' => undef,
2427                 @_,
2428         );
2430         if (not defined $arg{licensePoolId} ) { 
2431                 return ("function requires licensePoolId as parameter", 1);
2432         }
2434         # Fetch pool infos from Opsi server
2435     my $callobj = {
2436         method  => 'getLicensePool_hash',
2437         params  => [ $arg{licensePoolId} ],
2438         id  => 1,
2439     };
2440     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2442         # Check Opsi error
2443         my ($res_error, $res_error_str) = &check_opsi_res($res);
2444         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2446         return ($res->result, 0);
2449 sub _getSoftwareLicenses_listOfHashes {
2450         # Fetch licenses associated to the given pool
2451         my $callobj = {
2452                 method  => 'getSoftwareLicenses_listOfHashes',
2453                 params  => [ ],
2454                 id  => 1,
2455         };
2456         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2458         # Check Opsi error
2459         my ($res_error, $res_error_str) = &check_opsi_res($res);
2460         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2462         return ($res->result, 0);
2465 sub _getSoftwareLicenseUsages_listOfHashes {
2466         my %arg = (
2467                         'hostId' => "",
2468                         'licensePoolId' => "",
2469                         @_,
2470                         );
2472         # Fetch pool infos from Opsi server
2473         my $callobj = {
2474                 method  => 'getSoftwareLicenseUsages_listOfHashes',
2475                 params  => [ $arg{hostId}, $arg{licensePoolId} ],
2476                 id  => 1,
2477         };
2478         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2480         # Check Opsi error
2481         my ($res_error, $res_error_str) = &check_opsi_res($res);
2482         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2484         return ($res->result, 0);
2487 sub _removeSoftwareLicenseFromLicensePool {
2488         my %arg = (
2489                 'softwareLicenseId' => undef,
2490                 'licensePoolId' => undef,
2491                 @_,
2492                 );
2494         if (not defined $arg{softwareLicenseId} ) { 
2495                 return ("function requires softwareLicenseId as parameter", 1);
2496                 }
2497                 if (not defined $arg{licensePoolId} ) { 
2498                 return ("function requires licensePoolId as parameter", 1);
2499         }
2501         # Remove software license from license pool
2502         my $callobj = {
2503                 method  => 'removeSoftwareLicenseFromLicensePool',
2504                 params  => [ $arg{softwareLicenseId}, $arg{licensePoolId} ],
2505                 id  => 1,
2506         };
2507         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2509         # Check Opsi error
2510         my ($res_error, $res_error_str) = &check_opsi_res($res);
2511         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2513         return ($res->result, 0);
2516 sub _deleteSoftwareLicense {
2517         my %arg = (
2518                 'softwareLicenseId' => undef,
2519                 'removeFromPools' => "false",
2520                 @_,
2521                 );
2523         if (not defined $arg{softwareLicenseId} ) { 
2524                 return ("function requires softwareLicenseId as parameter", 1);
2525         }
2526         my $removeFromPools = "";
2527         if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) { 
2528                 $removeFromPools = "removeFromPools";
2529         }
2531         # Fetch
2532         my $callobj = {
2533                 method  => 'deleteSoftwareLicense',
2534                 params  => [ $arg{softwareLicenseId}, $removeFromPools ],
2535                 id  => 1,
2536         };
2537         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2539         # Check Opsi error
2540         my ($res_error, $res_error_str) = &check_opsi_res($res);
2541         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2543         return ($res->result, 0);
2546 sub _getLicensePoolId {
2547         my %arg = (
2548                         'productId' => undef,
2549                         @_,
2550                         );
2551         
2552         if (not defined $arg{productId} ) {
2553                 return ("function requires productId as parameter", 1);
2554         }
2556     my $callobj = {
2557         method  => 'getLicensePoolId',
2558         params  => [ $arg{productId} ],
2559         id  => 1,
2560     };
2561     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2563         # Check Opsi error
2564         my ($res_error, $res_error_str) = &check_opsi_res($res);
2565         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2567         return ($res->result, 0);
2570 sub _getLicenseContract_hash {
2571         my %arg = (
2572                         'licenseContractId' => undef,
2573                         @_,
2574                         );
2575         
2576         if (not defined $arg{licenseContractId} ) {
2577                 return ("function requires licenseContractId as parameter", 1);
2578         }
2580     my $callobj = {
2581         method  => 'getLicenseContract_hash',
2582         params  => [ $arg{licenseContractId} ],
2583         id  => 1,
2584     };
2585     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2587         # Check Opsi error
2588         my ($res_error, $res_error_str) = &check_opsi_res($res);
2589         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2591         return ($res->result, 0);
2594 sub _createLicenseContract {
2595         my %arg = (
2596                         'licenseContractId' => undef,
2597                         'partner' => undef,
2598                         'conclusionDate' => undef,
2599                         'notificationDate' => undef,
2600                         'expirationDate' => undef,
2601                         'notes' => undef,
2602                         @_,
2603                         );
2605         # Create license contract at Opsi server
2606     my $callobj = {
2607         method  => 'createLicenseContract',
2608         params  => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2609         id  => 1,
2610     };
2611     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2613         # Check Opsi error
2614         my ($res_error, $res_error_str) = &check_opsi_res($res);
2615         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2617         return ($res->result, 0);
2620 sub _createSoftwareLicense {
2621         my %arg = (
2622                         'softwareLicenseId' => undef,
2623                         'licenseContractId' => undef,
2624                         'licenseType' => undef,
2625                         'maxInstallations' => undef,
2626                         'boundToHost' => undef,
2627                         'expirationDate' => undef,
2628                         @_,
2629                         );
2631     my $callobj = {
2632         method  => 'createSoftwareLicense',
2633         params  => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2634         id  => 1,
2635     };
2636     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2638         # Check Opsi error
2639         my ($res_error, $res_error_str) = &check_opsi_res($res);
2640         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2642         return ($res->result, 0);
2645 sub _addSoftwareLicenseToLicensePool {
2646         my %arg = (
2647             'softwareLicenseId' => undef,
2648             'licensePoolId' => undef,
2649             'licenseKey' => undef,
2650             @_,
2651             );
2653         if (not defined $arg{softwareLicenseId} ) {
2654                 return ("function requires softwareLicenseId as parameter", 1);
2655         }
2656         if (not defined $arg{licensePoolId} ) {
2657                 return ("function requires licensePoolId as parameter", 1);
2658         }
2660         # Add software license to license pool
2661         my $callobj = {
2662         method  => 'addSoftwareLicenseToLicensePool',
2663         params  => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ],
2664         id  => 1,
2665     };
2666     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2668         # Check Opsi error
2669         my ($res_error, $res_error_str) = &check_opsi_res($res);
2670         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2672         return ($res->result, 0);
2675 1;