Code

* move all opsi related stuff from gosa-si-server to opsi_com.pm
[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_getAllSoftwareLicenses",
38         "opsi_removeLicense",
39         "opsi_getReservedLicenses",
40         "opsi_boundHostToLicense",
41         "opsi_unboundHostFromLicense",
42         "opsi_test",
43    );
44 @EXPORT = @events;
46 use strict;
47 use warnings;
48 use GOSA::GosaSupportDaemon;
49 use Data::Dumper;
50 use XML::Quote qw(:all);
52 BEGIN {}
54 END {}
56 # ----------------------------------------------------------------------------
57 #                          D E C L A R A T I O N S
58 # ----------------------------------------------------------------------------
60 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
61 my ($opsi_enabled, $opsi_server, $opsi_admin, $opsi_password, $opsi_url, $opsi_client);
62 my %cfg_defaults = (
63                 "Opsi" => {
64                 "enabled"  => [\$opsi_enabled, "false"],
65                 "server"   => [\$opsi_server, "localhost"],
66                 "admin"    => [\$opsi_admin, "opsi-admin"],
67                 "password" => [\$opsi_password, "secret"],
68                 },
69 );
70 &read_configfile($main::cfg_file, %cfg_defaults);
71 if ($opsi_enabled eq "true") {
72         use JSON::RPC::Client;
73         use XML::Quote qw(:all);
74         use Time::HiRes qw( time );
75         $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
76         $opsi_client = new JSON::RPC::Client;
77 }
79 # ----------------------------------------------------------------------------
80 #   external methods handling the comunication with GOsa/GOsa-si
81 # ----------------------------------------------------------------------------
83 ################################
84 # @brief A function returning a list of functions which are exported by importing the module.
85 # @return List of all provided functions
86 sub get_events {
87     return \@events;
88 }
90 ################################
91 # @brief Adds an Opsi product to an Opsi client.
92 # @param msg - STRING - xml message with tags hostId and productId
93 # @param msg_hash - HASHREF - message information parsed into a hash
94 # @param session_id - INTEGER - POE session id of the processing of this message
95 # @return out_msg - STRING - feedback to GOsa in success and error case
96 sub opsi_add_product_to_client {
97         my $startTime = Time::HiRes::time;
98     my ($msg, $msg_hash, $session_id) = @_;
99     my $header = @{$msg_hash->{'header'}}[0];
100     my $source = @{$msg_hash->{'source'}}[0];
101     my $target = @{$msg_hash->{'target'}}[0];
102     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
104     # Build return message
105     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
106     if (defined $forward_to_gosa) {
107         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
108     }
110     # Sanity check of needed parameter
111     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
112                 return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
113     }
114     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
115                 return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
116     }
118         # Get hostId
119         my $hostId = @{$msg_hash->{'hostId'}}[0];
120         &add_content2xml_hash($out_hash, "hostId", $hostId);
122         # Get productID
123         my $productId = @{$msg_hash->{'productId'}}[0];
124         &add_content2xml_hash($out_hash, "productId", $productId);
126         # Do an action request for all these -> "setup".
127         my $callobj = {
128                 method  => 'setProductActionRequest',
129                 params  => [ $productId, $hostId, "setup" ],
130                 id  => 1, }; 
131         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
133         if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
135         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
136     return ( &create_xml_string($out_hash) );
139 ################################
140 # @brief Deletes an Opsi-product from an Opsi-client. 
141 # @param msg - STRING - xml message with tags hostId and productId
142 # @param msg_hash - HASHREF - message information parsed into a hash
143 # @param session_id - INTEGER - POE session id of the processing of this message
144 # @return out_msg - STRING - feedback to GOsa in success and error case
145 sub opsi_del_product_from_client {
146         my $startTime = Time::HiRes::time;
147     my ($msg, $msg_hash, $session_id) = @_;
148     my $header = @{$msg_hash->{'header'}}[0];
149     my $source = @{$msg_hash->{'source'}}[0];
150     my $target = @{$msg_hash->{'target'}}[0];
151     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
152     my ($hostId, $productId);
153     my $error = 0;
154     my ($sres, $sres_err, $sres_err_string);
156     # Build return message
157     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
158     if (defined $forward_to_gosa) {
159         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
160     }
162     # Sanity check of needed parameter
163     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
164         $error++;
165         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
166         &add_content2xml_hash($out_hash, "error", "hostId");
167         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
169     }
170     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
171         $error++;
172         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
173         &add_content2xml_hash($out_hash, "error", "productId");
174         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
175     }
177     # All parameter available
178     if (not $error) {
179         # Get hostId
180         $hostId = @{$msg_hash->{'hostId'}}[0];
181         &add_content2xml_hash($out_hash, "hostId", $hostId);
183         # Get productID
184         $productId = @{$msg_hash->{'productId'}}[0];
185         &add_content2xml_hash($out_hash, "productId", $productId);
187         # Check to get product action list 
188         my $callobj = {
189             method  => 'getPossibleProductActions_list',
190             params  => [ $productId ],
191             id  => 1, };
192         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
193         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
194         if ($sres_err){
195             &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
196             &add_content2xml_hash($out_hash, "error", $sres_err_string);
197             $error++;
198         }
199     }
201     # Check action uninstall of product
202     if (not $error) {
203         my $uninst_possible= 0;
204         foreach my $r (@{$sres->result}) {
205             if ($r eq 'uninstall') {
206                 $uninst_possible= 1;
207             }
208         }
209         if (!$uninst_possible){
210             &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
211             &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
212             $error++;
213         }
214     }
216     # Set product state to "none"
217     # Do an action request for all these -> "setup".
218     if (not $error) {
219         my $callobj = {
220             method  => 'setProductActionRequest',
221             params  => [ $productId, $hostId, "none" ],
222             id  => 1, 
223         }; 
224         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
225         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
226         if ($sres_err){
227             &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
228             &add_content2xml_hash($out_hash, "error", $sres_err_string);
229         }
230     }
232     # Return message
233         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
234     return ( &create_xml_string($out_hash) );
237 ################################
238 # @brief Adds an Opsi client to Opsi.
239 # @param msg - STRING - xml message with tags hostId and macaddress
240 # @param msg_hash - HASHREF - message information parsed into a hash
241 # @param session_id - INTEGER - POE session id of the processing of this message
242 # @return out_msg - STRING - feedback to GOsa in success and error case
243 sub opsi_add_client {
244         my $startTime = Time::HiRes::time;
245     my ($msg, $msg_hash, $session_id) = @_;
246     my $header = @{$msg_hash->{'header'}}[0];
247     my $source = @{$msg_hash->{'source'}}[0];
248     my $target = @{$msg_hash->{'target'}}[0];
249     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
250     my ($hostId, $mac);
251     my $error = 0;
252     my ($sres, $sres_err, $sres_err_string);
254     # Build return message with twisted target and source
255     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
256     if (defined $forward_to_gosa) {
257         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
258     }
260     # Sanity check of needed parameter
261     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
262         $error++;
263         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
264         &add_content2xml_hash($out_hash, "error", "hostId");
265         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
266     }
267     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
268         $error++;
269         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
270         &add_content2xml_hash($out_hash, "error", "macaddress");
271         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
272     }
274     if (not $error) {
275         # Get hostId
276         $hostId = @{$msg_hash->{'hostId'}}[0];
277         &add_content2xml_hash($out_hash, "hostId", $hostId);
279         # Get macaddress
280         $mac = @{$msg_hash->{'macaddress'}}[0];
281         &add_content2xml_hash($out_hash, "macaddress", $mac);
283         my $name= $hostId;
284         $name=~ s/^([^.]+).*$/$1/;
285         my $domain= $hostId;
286         $domain=~ s/^[^.]+\.(.*)$/$1/;
287         my ($description, $notes, $ip);
289         if (defined @{$msg_hash->{'description'}}[0]){
290             $description = @{$msg_hash->{'description'}}[0];
291         }
292         if (defined @{$msg_hash->{'notes'}}[0]){
293             $notes = @{$msg_hash->{'notes'}}[0];
294         }
295         if (defined @{$msg_hash->{'ip'}}[0]){
296             $ip = @{$msg_hash->{'ip'}}[0];
297         }
299         my $callobj;
300         $callobj = {
301             method  => 'createClient',
302             params  => [ $name, $domain, $description, $notes, $ip, $mac ],
303             id  => 1,
304         };
306         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
307         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
308         if ($sres_err){
309             &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
310             &add_content2xml_hash($out_hash, "error", $sres_err_string);
311         } else {
312             &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5); 
313         }
314     }
316     # Return message
317         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
318     return ( &create_xml_string($out_hash) );
321 ################################
322 # @brief Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
323 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
324 # @param msg_hash - HASHREF - message information parsed into a hash
325 # @param session_id - INTEGER - POE session id of the processing of this message    
326 # @return out_msg - STRING - feedback to GOsa in success and error case
327 sub opsi_modify_client {
328         my $startTime = Time::HiRes::time;
329     my ($msg, $msg_hash, $session_id) = @_;
330     my $header = @{$msg_hash->{'header'}}[0];
331     my $source = @{$msg_hash->{'source'}}[0];
332     my $target = @{$msg_hash->{'target'}}[0];
333     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
334     my $hostId;
335     my $error = 0;
336     my ($sres, $sres_err, $sres_err_string);
338     # Build return message with twisted target and source
339     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
340     if (defined $forward_to_gosa) {
341         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
342     }
344     # Sanity check of needed parameter
345     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
346         $error++;
347         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
348         &add_content2xml_hash($out_hash, "error", "hostId");
349         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
350     }
352     if (not $error) {
353         # Get hostId
354         $hostId = @{$msg_hash->{'hostId'}}[0];
355         &add_content2xml_hash($out_hash, "hostId", $hostId);
356         my $name= $hostId;
357         $name=~ s/^([^.]+).*$/$1/;
358         my $domain= $hostId;
359         $domain=~ s/^[^.]+(.*)$/$1/;
361         # Modify description, notes or mac if defined
362         my ($description, $notes, $mac);
363         my $callobj;
364         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
365             $description = @{$msg_hash->{'description'}}[0];
366             $callobj = {
367                 method  => 'setHostDescription',
368                 params  => [ $hostId, $description ],
369                 id  => 1,
370             };
371             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
372             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
373             if ($sres_err){
374                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
375                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
376             }
377         }
378         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
379             $notes = @{$msg_hash->{'notes'}}[0];
380             $callobj = {
381                 method  => 'setHostNotes',
382                 params  => [ $hostId, $notes ],
383                 id  => 1,
384             };
385             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
386             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
387             if ($sres_err){
388                 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
389                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
390             }
391         }
392         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
393             $mac = @{$msg_hash->{'mac'}}[0];
394             $callobj = {
395                 method  => 'setMacAddress',
396                 params  => [ $hostId, $mac ],
397                 id  => 1,
398             };
399             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
400             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
401             if ($sres_err){
402                 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
403                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
404             }
405         }
406     }
408     # Return message
409         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
410     return ( &create_xml_string($out_hash) );
412  
413 ################################
414 # @brief Get netboot products for specific host.
415 # @param msg - STRING - xml message with tag hostId
416 # @param msg_hash - HASHREF - message information parsed into a hash
417 # @param session_id - INTEGER - POE session id of the processing of this message
418 # @return out_msg - STRING - feedback to GOsa in success and error case
419 sub opsi_get_netboot_products {
420         my $startTime = Time::HiRes::time;
421     my ($msg, $msg_hash, $session_id) = @_;
422     my $header = @{$msg_hash->{'header'}}[0];
423     my $source = @{$msg_hash->{'source'}}[0];
424     my $target = @{$msg_hash->{'target'}}[0];
425     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
426     my $hostId;
427     my $xml_msg;
429     # Build return message with twisted target and source
430     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
431     if (defined $forward_to_gosa) {
432         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
433     }
435     # Get hostId if defined
436     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
437         $hostId = @{$msg_hash->{'hostId'}}[0];
438         &add_content2xml_hash($out_hash, "hostId", $hostId);
439     }
441     &add_content2xml_hash($out_hash, "xxx", "");
442     $xml_msg = &create_xml_string($out_hash);
443     # For hosts, only return the products that are or get installed
444     my $callobj;
445     $callobj = {
446         method  => 'getNetBootProductIds_list',
447         params  => [ ],
448         id  => 1,
449     };
450     &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
451     &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
452     &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
453     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
454     &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
455     my %r = ();
456     for (@{$res->result}) { $r{$_} = 1 }
458       if (not &check_opsi_res($res)){
460         if (defined $hostId){
462             $callobj = {
463                 method  => 'getProductStates_hash',
464                 params  => [ $hostId ],
465                 id  => 1,
466             };
468             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
469             if (not &check_opsi_res($hres)){
470                 my $htmp= $hres->result->{$hostId};
472                 # check state != not_installed or action == setup -> load and add
473                 foreach my $product (@{$htmp}){
475                     if (!defined ($r{$product->{'productId'}})){
476                         next;
477                     }
479                     # Now we've a couple of hashes...
480                     if ($product->{'installationStatus'} ne "not_installed" or
481                             $product->{'actionRequest'} eq "setup"){
482                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
484                         $callobj = {
485                             method  => 'getProduct_hash',
486                             params  => [ $product->{'productId'} ],
487                             id  => 1,
488                         };
490                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
491                         if (not &check_opsi_res($sres)){
492                             my $tres= $sres->result;
494                             my $name= xml_quote($tres->{'name'});
495                             my $r= $product->{'productId'};
496                             my $description= xml_quote($tres->{'description'});
497                             $name=~ s/\//\\\//;
498                             $description=~ s/\//\\\//;
499                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
500                         }
501                     }
502                 }
504             }
506         } else {
507             foreach my $r (@{$res->result}) {
508                 $callobj = {
509                     method  => 'getProduct_hash',
510                     params  => [ $r ],
511                     id  => 1,
512                 };
514                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
515                 if (not &check_opsi_res($sres)){
516                     my $tres= $sres->result;
518                     my $name= xml_quote($tres->{'name'});
519                     my $description= xml_quote($tres->{'description'});
520                     $name=~ s/\//\\\//;
521                     $description=~ s/\//\\\//;
522                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
523                 }
524             }
526         }
527     }
528     $xml_msg=~ s/<xxx><\/xxx>//;
530     # Return message
531         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
532     return ( $xml_msg );
535 ################################   
536 # @brief Get product properties for a product and a specific host or gobally for a product.
537 # @param msg - STRING - xml message with tags productId and optional hostId
538 # @param msg_hash - HASHREF - message information parsed into a hash
539 # @param session_id - INTEGER - POE session id of the processing of this message
540 # @return out_msg - STRING - feedback to GOsa in success and error case
541 sub opsi_get_product_properties {
542         my $startTime = Time::HiRes::time;
543     my ($msg, $msg_hash, $session_id) = @_;
544     my $header = @{$msg_hash->{'header'}}[0];
545     my $source = @{$msg_hash->{'source'}}[0];
546     my $target = @{$msg_hash->{'target'}}[0];
547     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
548     my ($hostId, $productId);
549     my $xml_msg;
551     # Build return message with twisted target and source
552     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
553     if (defined $forward_to_gosa) {
554         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
555     }
557     # Sanity check of needed parameter
558     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
559         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
560         &add_content2xml_hash($out_hash, "error", "productId");
561         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
563         # Return message
564         return ( &create_xml_string($out_hash) );
565     }
567     # Get productid
568     $productId = @{$msg_hash->{'productId'}}[0];
569     &add_content2xml_hash($out_hash, "producId", "$productId");
571     # Get hostId if defined
572     if (defined @{$msg_hash->{'hostId'}}[0]){
573       $hostId = @{$msg_hash->{'hostId'}}[0];
574       &add_content2xml_hash($out_hash, "hostId", $hostId);
575     }
577     # Load actions
578     my $callobj = {
579       method  => 'getPossibleProductActions_list',
580       params  => [ $productId ],
581       id  => 1,
582     };
583     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
584     if (not &check_opsi_res($res)){
585       foreach my $action (@{$res->result}){
586         &add_content2xml_hash($out_hash, "action", $action);
587       }
588     }
590     # Add place holder
591     &add_content2xml_hash($out_hash, "xxx", "");
593     # Move to XML string
594     $xml_msg= &create_xml_string($out_hash);
596     # JSON Query
597     if (defined $hostId){
598       $callobj = {
599           method  => 'getProductProperties_hash',
600           params  => [ $productId, $hostId ],
601           id  => 1,
602       };
603     } else {
604       $callobj = {
605           method  => 'getProductProperties_hash',
606           params  => [ $productId ],
607           id  => 1,
608       };
609     }
610     $res = $main::opsi_client->call($main::opsi_url, $callobj);
612     # JSON Query 2
613     $callobj = {
614       method  => 'getProductPropertyDefinitions_listOfHashes',
615       params  => [ $productId ],
616       id  => 1,
617     };
619     # Assemble options
620     my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
621     my $values = {};
622     my $descriptions = {};
623     if (not &check_opsi_res($res2)){
624         my $r= $res2->result;
626           foreach my $entr (@$r){
627             # Unroll values
628             my $cnv;
629             if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
630               foreach my $v (@{$entr->{'values'}}){
631                 $cnv.= "<value>$v</value>";
632               }
633             } else {
634               $cnv= $entr->{'values'};
635             }
636             $values->{$entr->{'name'}}= $cnv;
637             $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
638           }
639     }
641     if (not &check_opsi_res($res)){
642         my $r= $res->result;
643         foreach my $key (keys %{$r}) {
644             my $item= "\n<item>";
645             my $value= $r->{$key};
646             my $dsc= "";
647             my $vals= "";
648             if (defined $descriptions->{$key}){
649               $dsc= $descriptions->{$key};
650             }
651             if (defined $values->{$key}){
652               $vals= $values->{$key};
653             }
654             $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
655             $item.= "</item>";
656             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
657         }
658     }
660     $xml_msg=~ s/<xxx><\/xxx>//;
662     # Return message
663         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
664     return ( $xml_msg );
667 ################################   
668 # @brief 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.
669 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
670 # @param msg_hash - HASHREF - message information parsed into a hash
671 # @param session_id - INTEGER - POE session id of the processing of this message
672 # @return out_msg - STRING - feedback to GOsa in success and error case
673 sub opsi_set_product_properties {
674         my $startTime = Time::HiRes::time;
675     my ($msg, $msg_hash, $session_id) = @_;
676     my $header = @{$msg_hash->{'header'}}[0];
677     my $source = @{$msg_hash->{'source'}}[0];
678     my $target = @{$msg_hash->{'target'}}[0];
679     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
680     my ($productId, $hostId);
682     # Build return message with twisted target and source
683     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
684     if (defined $forward_to_gosa) {
685         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
686     }
688     # Sanity check of needed parameter
689     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
690         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
691         &add_content2xml_hash($out_hash, "error", "productId");
692         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
693         return ( &create_xml_string($out_hash) );
694     }
695     if (not exists $msg_hash->{'item'}) {
696         &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
697         &add_content2xml_hash($out_hash, "error", "item");
698         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
699         return ( &create_xml_string($out_hash) );
700     } else {
701         if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
702             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
703             &add_content2xml_hash($out_hash, "error", "name");
704             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
705             return ( &create_xml_string($out_hash) );
706         }
707         if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
708             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
709             &add_content2xml_hash($out_hash, "error", "value");
710             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
711             return ( &create_xml_string($out_hash) );
712         }
713     }
714     # if no hostId is given, set_product_properties will act on globally
715     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
716         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
717         &add_content2xml_hash($out_hash, "error", "hostId");
718         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
719         return ( &create_xml_string($out_hash) );
720     }
722         
723     # Get productId
724     $productId =  @{$msg_hash->{'productId'}}[0];
725     &add_content2xml_hash($out_hash, "productId", $productId);
727     # Get hostId if defined
728     if (exists $msg_hash->{'hostId'}){
729         $hostId = @{$msg_hash->{'hostId'}}[0];
730         &add_content2xml_hash($out_hash, "hostId", $hostId);
731     }
733     # Set product states if requested
734     if (defined @{$msg_hash->{'action'}}[0]){
735         &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
736     }
737     if (defined @{$msg_hash->{'state'}}[0]){
738         &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
739     }
741     # Find properties
742     foreach my $item (@{$msg_hash->{'item'}}){
743         # JSON Query
744         my $callobj;
746         if (defined $hostId){
747             $callobj = {
748                 method  => 'setProductProperty',
749                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
750                 id  => 1,
751             };
752         } else {
753             $callobj = {
754                 method  => 'setProductProperty',
755                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
756                 id  => 1,
757             };
758         }
760         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
761         my ($res_err, $res_err_string) = &check_opsi_res($res);
763         if ($res_err){
764             &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
765             &add_content2xml_hash($out_hash, "error", $res_err_string);
766         }
767     }
770     # Return message
771         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
772     return ( &create_xml_string($out_hash) );
775 ################################   
776 # @brief Reports client hardware inventory.
777 # @param msg - STRING - xml message with tag hostId
778 # @param msg_hash - HASHREF - message information parsed into a hash
779 # @param session_id - INTEGER - POE session id of the processing of this message
780 # @return out_msg - STRING - feedback to GOsa in success and error case
781 sub opsi_get_client_hardware {
782         my $startTime = Time::HiRes::time;
783     my ($msg, $msg_hash, $session_id) = @_;
784     my $header = @{$msg_hash->{'header'}}[0];
785     my $source = @{$msg_hash->{'source'}}[0];
786     my $target = @{$msg_hash->{'target'}}[0];
787     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
788     my $hostId;
789     my $error = 0;
790     my $xml_msg;
792     # Sanity check of needed parameter
793         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
794         $hostId = @{$msg_hash->{'hostId'}}[0];
795         } else {
796                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
797         }
800     # Build return message with twisted target and source
801     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
802     if (defined $forward_to_gosa) {
803       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
804     }
805         &add_content2xml_hash($out_hash, "hostId", "$hostId");
806         &add_content2xml_hash($out_hash, "xxx", "");
808     # Move to XML string
809     $xml_msg= &create_xml_string($out_hash);
810     
811         my $res = &_callOpsi(method=>'getHardwareInformation_hash', params=>[ $hostId ]);
812         if (not &check_opsi_res($res)){
813                 my $result= $res->result;
814                 if (ref $result eq "HASH") {
815                         foreach my $r (keys %{$result}){
816                                 my $item= "\n<item><id>".xml_quote($r)."</id>";
817                                 my $value= $result->{$r};
818                                 foreach my $sres (@{$value}){
820                                         foreach my $dres (keys %{$sres}){
821                                                 if (defined $sres->{$dres}){
822                                                         $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
823                                                 }
824                                         }
826                                 }
827                                 $item.= "</item>";
828                                 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
830                         }
831                 }
832         }
834         $xml_msg=~ s/<xxx><\/xxx>//;
836     # Return message
837         my $endTime = Time::HiRes::time;
838         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
839         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
840     return ( $xml_msg );
843 ################################   
844 # @brief Reports all Opsi clients. 
845 # @param msg - STRING - xml message 
846 # @param msg_hash - HASHREF - message information parsed into a hash
847 # @param session_id - INTEGER - POE session id of the processing of this message
848 # @return out_msg - STRING - feedback to GOsa in success and error case
849 sub opsi_list_clients {
850         my $startTime = Time::HiRes::time;
851     my ($msg, $msg_hash, $session_id) = @_;
852     my $header = @{$msg_hash->{'header'}}[0];
853     my $source = @{$msg_hash->{'source'}}[0];
854     my $target = @{$msg_hash->{'target'}}[0];
855     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
857     # Build return message with twisted target and source
858     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
859     if (defined $forward_to_gosa) {
860       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
861     }
862     &add_content2xml_hash($out_hash, "xxx", "");
864     # Move to XML string
865     my $xml_msg= &create_xml_string($out_hash);
867     # JSON Query
868     my $callobj = {
869         method  => 'getClients_listOfHashes',
870         params  => [ ],
871         id  => 1,
872     };
873     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
874     if (not &check_opsi_res($res)){
875         foreach my $host (@{$res->result}){
876             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
877             if (defined($host->{'description'})){
878                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
879             }
880             if (defined($host->{'notes'})){
881                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
882             }
883             if (defined($host->{'lastSeen'})){
884                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
885             }
887             $callobj = {
888               method  => 'getIpAddress',
889               params  => [ $host->{'hostId'} ],
890               id  => 1,
891             };
892             my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
893             if ( not &check_opsi_res($sres)){
894               $item.= "<ip>".xml_quote($sres->result)."</ip>";
895             }
897             $callobj = {
898               method  => 'getMacAddress',
899               params  => [ $host->{'hostId'} ],
900               id  => 1,
901             };
902             $sres= $main::opsi_client->call($main::opsi_url, $callobj);
903             if ( not &check_opsi_res($sres)){
904                 $item.= "<mac>".xml_quote($sres->result)."</mac>";
905             }
906             $item.= "</item>";
907             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
908         }
909     }
910     $xml_msg=~ s/<xxx><\/xxx>//;
912         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
913     return ( $xml_msg );
916 ################################   
917 # @brief Reports client software inventory.
918 # @param msg - STRING - xml message with tag hostId
919 # @param msg_hash - HASHREF - message information parsed into a hash
920 # @param session_id - INTEGER - POE session id of the processing of this message
921 # @return out_msg - STRING - feedback to GOsa in success and error case
922 sub opsi_get_client_software {
923         my $startTime = Time::HiRes::time;
924     my ($msg, $msg_hash, $session_id) = @_;
925     my $header = @{$msg_hash->{'header'}}[0];
926     my $source = @{$msg_hash->{'source'}}[0];
927     my $target = @{$msg_hash->{'target'}}[0];
928     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
929     my $error = 0;
930     my $hostId;
931     my $xml_msg;
933     # Build return message with twisted target and source
934     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
935     if (defined $forward_to_gosa) {
936       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
937     }
939     # Sanity check of needed parameter
940     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
941         $error++;
942         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
943         &add_content2xml_hash($out_hash, "error", "hostId");
944         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
945     }
947     if (not $error) {
949     # Get hostId
950         $hostId = @{$msg_hash->{'hostId'}}[0];
951         &add_content2xml_hash($out_hash, "hostId", "$hostId");
952         &add_content2xml_hash($out_hash, "xxx", "");
953     }
955     $xml_msg= &create_xml_string($out_hash);
957     if (not $error) {
959     # JSON Query
960         my $callobj = {
961             method  => 'getSoftwareInformation_hash',
962             params  => [ $hostId ],
963             id  => 1,
964         };
966         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
967         if (not &check_opsi_res($res)){
968             my $result= $res->result;
969         }
971         $xml_msg=~ s/<xxx><\/xxx>//;
973     }
975     # Return message
976         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
977     return ( $xml_msg );
980 ################################   
981 # @brief Reports product for given hostId or globally.
982 # @param msg - STRING - xml message with optional tag hostId
983 # @param msg_hash - HASHREF - message information parsed into a hash
984 # @param session_id - INTEGER - POE session id of the processing of this message
985 # @return out_msg - STRING - feedback to GOsa in success and error case
986 sub opsi_get_local_products {
987         my $startTime = Time::HiRes::time;
988     my ($msg, $msg_hash, $session_id) = @_;
989     my $header = @{$msg_hash->{'header'}}[0];
990     my $source = @{$msg_hash->{'source'}}[0];
991     my $target = @{$msg_hash->{'target'}}[0];
992     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
993     my $hostId;
995     # Build return message with twisted target and source
996     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
997     if (defined $forward_to_gosa) {
998         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
999     }
1000     &add_content2xml_hash($out_hash, "xxx", "");
1002     # Get hostId if defined
1003     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
1004         $hostId = @{$msg_hash->{'hostId'}}[0];
1005         &add_content2xml_hash($out_hash, "hostId", $hostId);
1006     }
1008     # Move to XML string
1009     my $xml_msg= &create_xml_string($out_hash);
1011     # For hosts, only return the products that are or get installed
1012     my $callobj;
1013     $callobj = {
1014         method  => 'getLocalBootProductIds_list',
1015         params  => [ ],
1016         id  => 1,
1017     };
1019     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1020     my %r = ();
1021     for (@{$res->result}) { $r{$_} = 1 }
1023     if (not &check_opsi_res($res)){
1025         if (defined $hostId){
1026             $callobj = {
1027                 method  => 'getProductStates_hash',
1028                 params  => [ $hostId ],
1029                 id  => 1,
1030             };
1032             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1033             if (not &check_opsi_res($hres)){
1034                 my $htmp= $hres->result->{$hostId};
1036                 # Check state != not_installed or action == setup -> load and add
1037                 foreach my $product (@{$htmp}){
1039                     if (!defined ($r{$product->{'productId'}})){
1040                         next;
1041                     }
1043                     # Now we've a couple of hashes...
1044                     if ($product->{'installationStatus'} ne "not_installed" or
1045                             $product->{'actionRequest'} eq "setup"){
1046                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1048                         $callobj = {
1049                             method  => 'getProduct_hash',
1050                             params  => [ $product->{'productId'} ],
1051                             id  => 1,
1052                         };
1054                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1055                         if (not &check_opsi_res($sres)){
1056                             my $tres= $sres->result;
1058                             my $name= xml_quote($tres->{'name'});
1059                             my $r= $product->{'productId'};
1060                             my $description= xml_quote($tres->{'description'});
1061                             $name=~ s/\//\\\//;
1062                             $description=~ s/\//\\\//;
1063                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1064                         }
1066                     }
1067                 }
1069             }
1071         } else {
1072             foreach my $r (@{$res->result}) {
1073                 $callobj = {
1074                     method  => 'getProduct_hash',
1075                     params  => [ $r ],
1076                     id  => 1,
1077                 };
1079                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1080                 if (not &check_opsi_res($sres)){
1081                     my $tres= $sres->result;
1083                     my $name= xml_quote($tres->{'name'});
1084                     my $description= xml_quote($tres->{'description'});
1085                     $name=~ s/\//\\\//;
1086                     $description=~ s/\//\\\//;
1087                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1088                 }
1090             }
1092         }
1093     }
1095     $xml_msg=~ s/<xxx><\/xxx>//;
1097     # Retrun Message
1098         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1099     return ( $xml_msg );
1102 ################################   
1103 # @brief Deletes a client from Opsi.
1104 # @param msg - STRING - xml message with tag hostId
1105 # @param msg_hash - HASHREF - message information parsed into a hash
1106 # @param session_id - INTEGER - POE session id of the processing of this message
1107 # @return out_msg - STRING - feedback to GOsa in success and error case
1108 sub opsi_del_client {
1109         my $startTime = Time::HiRes::time;
1110     my ($msg, $msg_hash, $session_id) = @_;
1111     my $header = @{$msg_hash->{'header'}}[0];
1112     my $source = @{$msg_hash->{'source'}}[0];
1113     my $target = @{$msg_hash->{'target'}}[0];
1114     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1115     my $hostId;
1116     my $error = 0;
1118     # Build return message with twisted target and source
1119     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1120     if (defined $forward_to_gosa) {
1121       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1122     }
1124     # Sanity check of needed parameter
1125     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1126         $error++;
1127         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1128         &add_content2xml_hash($out_hash, "error", "hostId");
1129         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1130     }
1132     if (not $error) {
1134     # Get hostId
1135         $hostId = @{$msg_hash->{'hostId'}}[0];
1136         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1138     # JSON Query
1139         my $callobj = {
1140             method  => 'deleteClient',
1141             params  => [ $hostId ],
1142             id  => 1,
1143         };
1144         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1145     }
1147     # Move to XML string
1148     my $xml_msg= &create_xml_string($out_hash);
1150     # Return message
1151         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1152     return ( $xml_msg );
1155 ################################   
1156 # @brief Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1157 # @param msg - STRING - xml message with tags hostId, macaddress
1158 # @param msg_hash - HASHREF - message information parsed into a hash
1159 # @param session_id - INTEGER - POE session id of the processing of this message
1160 # @return out_msg - STRING - feedback to GOsa in success and error case
1161 sub opsi_install_client {
1162         my $startTime = Time::HiRes::time;
1163     my ($msg, $msg_hash, $session_id) = @_;
1164     my $header = @{$msg_hash->{'header'}}[0];
1165     my $source = @{$msg_hash->{'source'}}[0];
1166     my $target = @{$msg_hash->{'target'}}[0];
1167     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1168     my ($hostId, $macaddress);
1169     my $error = 0;
1170     my @out_msg_l;
1172     # Build return message with twisted target and source
1173     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1174     if (defined $forward_to_gosa) {
1175         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1176     }
1178     # Sanity check of needed parameter
1179     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1180         $error++;
1181         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1182         &add_content2xml_hash($out_hash, "error", "hostId");
1183         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1184     }
1185     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
1186         $error++;
1187         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1188         &add_content2xml_hash($out_hash, "error", "macaddress");
1189         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1190     } else {
1191         if ((exists $msg_hash->{'macaddress'}) && 
1192                 ($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)) {  
1193             $macaddress = $1; 
1194         } else { 
1195             $error ++; 
1196             &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1197             &add_content2xml_hash($out_hash, "error", "macaddress");
1198             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1199         }
1200     }
1202     if (not $error) {
1204     # Get hostId
1205         $hostId = @{$msg_hash->{'hostId'}}[0];
1206         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1208         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1209         my $callobj = {
1210             method  => 'getProductStates_hash',
1211             params  => [ $hostId ],
1212             id  => 1,
1213         };
1215         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1216         if (not &check_opsi_res($hres)){
1217             my $htmp= $hres->result->{$hostId};
1219             # check state != not_installed or action == setup -> load and add
1220             foreach my $product (@{$htmp}){
1221                 # Now we've a couple of hashes...
1222                 if ($product->{'installationStatus'} ne "not_installed" or
1223                         $product->{'actionRequest'} ne "none"){
1225                     # Do an action request for all these -> "setup".
1226                     $callobj = {
1227                         method  => 'setProductActionRequest',
1228                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1229                         id  => 1,
1230                     };
1231                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1232                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1233                     if ($res_err){
1234                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1235                     } else {
1236                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1237                     }
1238                 }
1239             }
1240         }
1241         push(@out_msg_l, &create_xml_string($out_hash));
1242     
1244     # Build wakeup message for client
1245         if (not $error) {
1246             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1247             &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1248             my $wakeup_msg = &create_xml_string($wakeup_hash);
1249             push(@out_msg_l, $wakeup_msg);
1251             # invoke trigger wake for this gosa-si-server
1252             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1253         }
1254     }
1255     
1256     # Return messages
1257         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1258     return @out_msg_l;
1261 ################################
1262 # @brief Set action for an Opsi client
1263 # @param product - STRING - Opsi product
1264 # @param action - STRING - action
1265 # @param hostId - STRING - Opsi hostId
1266 sub _set_action {
1267   my $product= shift;
1268   my $action = shift;
1269   my $hostId = shift;
1270   my $callobj;
1272   $callobj = {
1273     method  => 'setProductActionRequest',
1274     params  => [ $product, $hostId, $action],
1275     id  => 1,
1276   };
1278   $main::opsi_client->call($main::opsi_url, $callobj);
1281 ################################
1282 # @brief Set state for an Opsi client
1283 # @param product - STRING - Opsi product
1284 # @param action - STRING - state
1285 # @param hostId - STRING - Opsi hostId
1286 sub _set_state {
1287   my $product = shift;
1288   my $state = shift;
1289   my $hostId = shift;
1290   my $callobj;
1292   $callobj = {
1293     method  => 'setProductState',
1294     params  => [ $product, $hostId, $state ],
1295     id  => 1,
1296   };
1298   $main::opsi_client->call($main::opsi_url, $callobj);
1301 ################################
1302 # @brief Create a license pool at Opsi server.
1303 # @param licensePoolId The name of the pool (optional). 
1304 # @param description The description of the pool (optional).
1305 # @param productIds A list of assigned porducts of the pool (optional). 
1306 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional). 
1307 sub opsi_createLicensePool {
1308         my $startTime = Time::HiRes::time;
1309     my ($msg, $msg_hash, $session_id) = @_;
1310     my $header = @{$msg_hash->{'header'}}[0];
1311     my $source = @{$msg_hash->{'source'}}[0];
1312     my $target = @{$msg_hash->{'target'}}[0];
1313         my $out_hash;
1314         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1315         my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1316         my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1317         my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1319         # Create license Pool
1320     my $callobj = {
1321         method  => 'createLicensePool',
1322         params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1323         id  => 1,
1324     };
1325     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1327         # Check Opsi error
1328         my ($res_error, $res_error_str) = &check_opsi_res($res);
1329         if ($res_error){
1330                 # Create error message
1331                 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1332                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1333                 return ( &create_xml_string($out_hash) );
1334         }
1336         # Create function result message
1337         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1338         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1340         my $endTime = Time::HiRes::time;
1341         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1342         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1343         return ( &create_xml_string($out_hash) );
1346 ################################
1347 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1348 sub opsi_getLicensePools_listOfHashes {
1349         my $startTime = Time::HiRes::time;
1350     my ($msg, $msg_hash, $session_id) = @_;
1351     my $header = @{$msg_hash->{'header'}}[0];
1352     my $source = @{$msg_hash->{'source'}}[0];
1353         my $out_hash;
1355         # Fetch infos from Opsi server
1356     my $callobj = {
1357         method  => 'getLicensePools_listOfHashes',
1358         params  => [ ],
1359         id  => 1,
1360     };
1361     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1363         # Check Opsi error
1364         my ($res_error, $res_error_str) = &check_opsi_res($res);
1365         if ($res_error){
1366                 # Create error message
1367                 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1368                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1369                 return ( &create_xml_string($out_hash) );
1370         }
1372         # Create function result message
1373         my $res_hash = { 'hit'=> [] };
1374         foreach my $licensePool ( @{$res->result}) {
1375                 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1376                         'description' => [$licensePool->{'description'}],
1377                         'productIds' => $licensePool->{'productIds'},
1378                         'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1379                         };
1380                 push( @{$res_hash->{hit}}, $licensePool_hash );
1381         }
1383         # Create function result message
1384         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1385         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1386         $out_hash->{result} = [$res_hash];
1388         my $endTime = Time::HiRes::time;
1389         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1390         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1391         return ( &create_xml_string($out_hash) );
1394 ################################
1395 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1396 # @param licensePoolId The name of the pool. 
1397 sub opsi_getLicensePool_hash {
1398         my $startTime = Time::HiRes::time;
1399     my ($msg, $msg_hash, $session_id) = @_;
1400     my $header = @{$msg_hash->{'header'}}[0];
1401     my $source = @{$msg_hash->{'source'}}[0];
1402     my $target = @{$msg_hash->{'target'}}[0];
1403     my $licensePoolId;
1404         my $out_hash;
1406         # Check input sanity
1407         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1408                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1409         } else {
1410                 return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
1411         }
1413         # Fetch infos from Opsi server
1414     my $callobj = {
1415         method  => 'getLicensePool_hash',
1416         params  => [ $licensePoolId ],
1417         id  => 1,
1418     };
1419     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1421         # Check Opsi error
1422         my ($res_error, $res_error_str) = &check_opsi_res($res);
1423         if ($res_error){
1424                 # Create error message
1425                 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1426                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1427                 &add_content2xml_hash($out_hash, "error", $res_error_str);
1428                 return ( &create_xml_string($out_hash) );
1429         }
1431         # Create function result message
1432         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1433         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1434         &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1435         &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1436         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1437         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1439         my $endTime = Time::HiRes::time;
1440         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1441         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1442         return ( &create_xml_string($out_hash) );
1445 sub _parse_getSoftwareLicenseUsages {
1446         my $res = shift;
1448         # Parse Opsi result
1449         my $tmp_licensePool_cache = {};
1450         my $res_hash = { 'hit'=> [] };
1451         foreach my $license ( @{$res}) {
1452                 my $tmp_licensePool = $license->{'licensePoolId'};
1453                 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1454                         # Fetch missing informations from Opsi and cache the results for a possible later usage
1455                         my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1456                         if (not $err) {
1457                                 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1458                         }
1459                 }
1460                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1461                         'notes' => [$license->{'notes'}],
1462                         'licenseKey' => [$license->{'licenseKey'}],
1463                         'hostId' => [$license->{'hostId'}],
1464                         'licensePoolId' => [$tmp_licensePool],
1465                         };
1466                 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1467                         $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1468                         map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1469                         map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1470                 }
1471                 push( @{$res_hash->{hit}}, $license_hash );
1472         }
1474         return $res_hash;
1477 ################################
1478 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1479 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1480 # @param licensePoolId The name of the pool (optional). 
1481 sub opsi_getSoftwareLicenseUsages {
1482         my $startTime = Time::HiRes::time;
1483         my ($msg, $msg_hash, $session_id) = @_;
1484         my $header = @{$msg_hash->{'header'}}[0];
1485         my $source = @{$msg_hash->{'source'}}[0];
1486         my $target = @{$msg_hash->{'target'}}[0];
1487         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1488         my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1489         my $out_hash;
1491         my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1492         if ($err){
1493                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1494         }
1496         # Parse Opsi result
1497         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1499         # Create function result message
1500         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1501         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1502         $out_hash->{result} = [$res_hash];
1504         my $endTime = Time::HiRes::time;
1505         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1506         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1507         return ( &create_xml_string($out_hash) );
1510 ################################
1511 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1512 # @param productId Something like 'firefox', 'python' or anything else .
1513 sub opsi_getSoftwareLicenseUsagesForProductId {
1514         my $startTime = Time::HiRes::time;
1515         my ($msg, $msg_hash, $session_id) = @_;
1516         my $header = @{$msg_hash->{'header'}}[0];
1517         my $source = @{$msg_hash->{'source'}}[0];
1519         # Check input sanity
1520         my $productId;
1521         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1522                 $productId= @{$msg_hash->{'productId'}}[0];
1523         } else {
1524                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1525         }
1527         # Fetch licensePoolId for productId
1528         my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1529         if ($err){
1530                 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1531         }
1533         my $licensePoolId;
1535         # Fetch softwareLiceceUsages for licensePoolId
1536         ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1537         if ($err){
1538                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1539         }
1541         # Parse Opsi result
1542         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1544         # Create function result message
1545         my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
1546         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1547         $out_hash->{result} = [$res_hash];
1549         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1550         return ( &create_xml_string($out_hash) );
1553 ################################
1554 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1555 # @param softwareLicenseId Identificator of a license.
1556 sub opsi_getSoftwareLicense_hash {
1557         my $startTime = Time::HiRes::time;
1558         my ($msg, $msg_hash, $session_id) = @_;
1559         my $header = @{$msg_hash->{'header'}}[0];
1560         my $source = @{$msg_hash->{'source'}}[0];
1561         my $target = @{$msg_hash->{'target'}}[0];
1562         my $softwareLicenseId;
1563         my $out_hash;
1565         # Check input sanity
1566         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1567                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1568         } else {
1569                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1570         }
1572         my $callobj = {
1573                 method  => 'getSoftwareLicense_hash',
1574                 params  => [ $softwareLicenseId ],
1575                 id  => 1,
1576         };
1577         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1579         # Check Opsi error
1580         my ($res_error, $res_error_str) = &check_opsi_res($res);
1581         if ($res_error){
1582                 # Create error message
1583                 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1584                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1585                 return ( &create_xml_string($out_hash) );
1586         }
1587         
1588         # Create function result message
1589         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1590         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1591         &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1592         &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1593         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1594         &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1595         foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1596                 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1597                 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1598         }
1600         my $endTime = Time::HiRes::time;
1601         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1602         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1603         return ( &create_xml_string($out_hash) );
1606 ################################
1607 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
1608 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted. 
1609 # @param licensePoolId The name of the pool. 
1610 sub opsi_deleteLicensePool {
1611         my $startTime = Time::HiRes::time;
1612         my ($msg, $msg_hash, $session_id) = @_;
1613     my $header = @{$msg_hash->{'header'}}[0];
1614     my $source = @{$msg_hash->{'source'}}[0];
1615     my $target = @{$msg_hash->{'target'}}[0];
1616     my $licensePoolId;
1617         my $out_hash;
1619         # Check input sanity
1620         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1621                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1622         } else {
1623                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1624         }
1626         # Fetch softwareLicenseIds used in license pool
1627         # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1628         # but not the license contracts of the software licenses. In our case each software license has exactly one license contract. 
1629         my $callobj = {
1630                 method  => 'getSoftwareLicenses_listOfHashes',
1631                 params  => [ ],
1632                 id  => 1,
1633         };
1634         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1636         # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1637         my @lCI_toBeDeleted;
1638         foreach my $softwareLicenseHash ( @{$res->result} ) {
1639                 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) { 
1640                         next; 
1641                 }  
1642                 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1643         }
1645         # Delete license pool at Opsi server
1646     $callobj = {
1647         method  => 'deleteLicensePool',
1648         params  => [ $licensePoolId, 'deleteLicenses=True'  ],
1649         id  => 1,
1650     };
1651     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1652         my ($res_error, $res_error_str) = &check_opsi_res($res);
1653         if ($res_error){
1654                 # Create error message
1655                 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1656                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1657                 return ( &create_xml_string($out_hash) );
1658         } 
1660         # Delete each license contract connected with the license pool
1661         foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1662                 my $callobj = {
1663                         method  => 'deleteLicenseContract',
1664                         params  => [ $licenseContractId ],
1665                         id  => 1,
1666                 };
1667                 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1668                 my ($res_error, $res_error_str) = &check_opsi_res($res);
1669                 if ($res_error){
1670                         # Create error message
1671                         &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1672                         $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1673                         return ( &create_xml_string($out_hash) );
1674                 }
1675         }
1677         # Create function result message
1678         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1679         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1681         my $endTime = Time::HiRes::time;
1682         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1683         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1684         return ( &create_xml_string($out_hash) );
1687 ################################
1688 # @brief Create a license contract, create a software license and add the software license to the license pool
1689 # @param licensePoolId The name of the pool the license should be assigned.
1690 # @param licenseKey The license key.
1691 # @param partner Name of the license partner (optional).
1692 # @param conclusionDate Date of conclusion of license contract (optional)
1693 # @param notificationDate Date of notification that license is running out soon (optional).
1694 # @param notes This is the place for some notes (optional)
1695 # @param softwareLicenseId Identificator of a license (optional).
1696 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1697 # @param maxInstallations The number of clients use this license (optional). 
1698 # @param boundToHost The name of the client the license is bound to (optional).
1699 # @param expirationDate The date when the license is running down (optional). 
1700 sub opsi_createLicense {
1701         my $startTime = Time::HiRes::time;
1702         my ($msg, $msg_hash, $session_id) = @_;
1703     my $header = @{$msg_hash->{'header'}}[0];
1704     my $source = @{$msg_hash->{'source'}}[0];
1705     my $target = @{$msg_hash->{'target'}}[0];
1706         my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1707         my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1708         my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1709         my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1710         my $licenseContractId = undef;
1711         my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1712         my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1713         my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1714         my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1715         my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1716         my $licensePoolId;
1717         my $licenseKey;
1718         my $out_hash;
1720         # Check input sanity
1721         if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1722                 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1723         } else {
1724                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1725         }
1726         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1727                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1728         } else {
1729                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1730         }
1731         if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1732                 return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
1733         }
1734         
1735         # Automatically define licenseContractId if ID is not given
1736         if (defined $softwareLicenseId) { 
1737                 $licenseContractId = "c_".$softwareLicenseId;
1738         }
1740         # Create license contract at Opsi server
1741     my $callobj = {
1742         method  => 'createLicenseContract',
1743         params  => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1744         id  => 1,
1745     };
1746     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1748         # Check Opsi error
1749         my ($res_error, $res_error_str) = &check_opsi_res($res);
1750         if ($res_error){
1751                 # Create error message
1752                 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1753                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1754                 return ( &create_xml_string($out_hash) );
1755         }
1756         
1757         $licenseContractId = $res->result;
1759         # Create software license at Opsi server
1760     $callobj = {
1761         method  => 'createSoftwareLicense',
1762         params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1763         id  => 1,
1764     };
1765     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1767         # Check Opsi error
1768         ($res_error, $res_error_str) = &check_opsi_res($res);
1769         if ($res_error){
1770                 # Create error message
1771                 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1772                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1773                 return ( &create_xml_string($out_hash) );
1774         }
1776         $softwareLicenseId = $res->result;
1778         # Add software license to license pool
1779         $callobj = {
1780         method  => 'addSoftwareLicenseToLicensePool',
1781         params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1782         id  => 1,
1783     };
1784     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1786         # Check Opsi error
1787         ($res_error, $res_error_str) = &check_opsi_res($res);
1788         if ($res_error){
1789                 # Create error message
1790                 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1791                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1792                 return ( &create_xml_string($out_hash) );
1793         }
1795         # Create function result message
1796         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1797         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1798         
1799         my $endTime = Time::HiRes::time;
1800         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1801         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1802         return ( &create_xml_string($out_hash) );
1805 ################################
1806 # @brief Assign a software license to a host
1807 # @param hostid Something like client_1.intranet.mydomain.de
1808 # @param licensePoolId The name of the pool.
1809 sub opsi_assignSoftwareLicenseToHost {
1810         my $startTime = Time::HiRes::time;
1811         my ($msg, $msg_hash, $session_id) = @_;
1812     my $header = @{$msg_hash->{'header'}}[0];
1813     my $source = @{$msg_hash->{'source'}}[0];
1814     my $target = @{$msg_hash->{'target'}}[0];
1815         my $hostId;
1816         my $licensePoolId;
1818         # Check input sanity
1819         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1820                 $hostId = @{$msg_hash->{'hostId'}}[0];
1821         } else {
1822                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1823         }
1824         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1825                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1826         } else {
1827                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1828         }
1830         # Assign a software license to a host
1831         my $callobj = {
1832         method  => 'getAndAssignSoftwareLicenseKey',
1833         params  => [ $hostId, $licensePoolId ],
1834         id  => 1,
1835     };
1836     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1838         # Check Opsi error
1839         my ($res_error, $res_error_str) = &check_opsi_res($res);
1840         if ($res_error){
1841                 # Create error message
1842                 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1843                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1844                 return ( &create_xml_string($out_hash) );
1845         }
1847         # Create function result message
1848         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1849         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1850         
1851         my $endTime = Time::HiRes::time;
1852         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1853         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1854         return ( &create_xml_string($out_hash) );
1857 ################################
1858 # @brief Unassign a software license from a host.
1859 # @param hostid Something like client_1.intranet.mydomain.de
1860 # @param licensePoolId The name of the pool.
1861 sub opsi_unassignSoftwareLicenseFromHost {
1862         my $startTime = Time::HiRes::time;
1863         my ($msg, $msg_hash, $session_id) = @_;
1864     my $header = @{$msg_hash->{'header'}}[0];
1865     my $source = @{$msg_hash->{'source'}}[0];
1866     my $target = @{$msg_hash->{'target'}}[0];
1867         my $hostId;
1868         my $licensePoolId;
1870         # Check input sanity
1871         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1872                 $hostId = @{$msg_hash->{'hostId'}}[0];
1873         } else {
1874                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1875         }
1876         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1877                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1878         } else {
1879                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1880         }
1882         # Unassign a software license from a host
1883         my $callobj = {
1884         method  => 'deleteSoftwareLicenseUsage',
1885         params  => [ $hostId, '', $licensePoolId ],
1886         id  => 1,
1887     };
1888     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1890         # Check Opsi error
1891         my ($res_error, $res_error_str) = &check_opsi_res($res);
1892         if ($res_error){
1893                 # Create error message
1894                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1895                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1896                 return ( &create_xml_string($out_hash) );
1897         }
1899         # Create function result message
1900         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1901         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1902         
1903         my $endTime = Time::HiRes::time;
1904         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1905         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1906         return ( &create_xml_string($out_hash) );
1909 ################################
1910 # @brief Unassign all software licenses from a host
1911 # @param hostid Something like client_1.intranet.mydomain.de
1912 sub opsi_unassignAllSoftwareLicensesFromHost {
1913         my $startTime = Time::HiRes::time;
1914         my ($msg, $msg_hash, $session_id) = @_;
1915     my $header = @{$msg_hash->{'header'}}[0];
1916     my $source = @{$msg_hash->{'source'}}[0];
1917     my $target = @{$msg_hash->{'target'}}[0];
1918         my $hostId;
1920         # Check input sanity
1921         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1922                 $hostId = @{$msg_hash->{'hostId'}}[0];
1923         } else {
1924                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1925         }
1927         # Unassign all software licenses from a host
1928         my $callobj = {
1929         method  => 'deleteAllSoftwareLicenseUsages',
1930         params  => [ $hostId ],
1931         id  => 1,
1932     };
1933     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1935         # Check Opsi error
1936         my ($res_error, $res_error_str) = &check_opsi_res($res);
1937         if ($res_error){
1938                 # Create error message
1939                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1940                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1941                 return ( &create_xml_string($out_hash) );
1942         }
1944         # Create function result message
1945         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1946         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1947         
1948         my $endTime = Time::HiRes::time;
1949         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1950         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1951         return ( &create_xml_string($out_hash) );
1955 ################################
1956 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1957 # and the number of max and remaining installations for a given OPSI product.
1958 # @param productId Identificator of an OPSI product.
1959 sub opsi_getLicenseInformationForProduct {
1960         my $startTime = Time::HiRes::time;
1961     my ($msg, $msg_hash, $session_id) = @_;
1962     my $header = @{$msg_hash->{'header'}}[0];
1963     my $source = @{$msg_hash->{'source'}}[0];
1964         my $productId;
1965         my $out_hash;
1967         # Check input sanity
1968         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1969                 $productId = @{$msg_hash->{'productId'}}[0];
1970         } else {
1971                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1972         }
1974         # Fetch infos from Opsi server
1975     my $callobj = {
1976         method  => 'getLicensePoolId',
1977         params  => [ $productId ],
1978         id  => 1,
1979     };
1980     #my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1981     my $res = $opsi_client->call($opsi_url, $callobj);
1983         # Check Opsi error
1984         my ($res_error, $res_error_str) = &check_opsi_res($res);
1985         if ($res_error){
1986                 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
1987         } 
1988         
1989         my $licensePoolId = $res->result;
1991         # Fetch statistic information for given pool ID
1992         $callobj = {
1993                 method  => 'getLicenseStatistics_hash',
1994                 params  => [ ],
1995                 id  => 1,
1996         };
1997         $res = $opsi_client->call($opsi_url, $callobj);
1999         # Check Opsi error
2000         ($res_error, $res_error_str) = &check_opsi_res($res);
2001         if ($res_error){
2002                 # Create error message
2003                 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
2004                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
2005                 return ( &create_xml_string($out_hash) );
2006         }
2008         # Create function result message
2009         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2010         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2011         &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
2012         &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
2013         &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
2014         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
2015         &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
2016         map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
2018         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2019         return ( &create_xml_string($out_hash) );
2023 ################################
2024 # @brief Returns licensePoolId, description, a list of productIds, al list of windowsSoftwareIds and a list of licenses for a given licensePoolId. 
2025 # Each license contains softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseKeys, hostIds, expirationDate, boundToHost and licenseContractId.
2026 # The licenseContract contains conclusionDate, expirationDate, notes, notificationDate and partner. 
2027 # @param licensePoolId The name of the pool.
2028 sub opsi_getPool {
2029         my $startTime = Time::HiRes::time;
2030     my ($msg, $msg_hash, $session_id) = @_;
2031     my $header = @{$msg_hash->{'header'}}[0];
2032     my $source = @{$msg_hash->{'source'}}[0];
2034         # Check input sanity
2035         my $licensePoolId;
2036         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2037                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2038         } else {
2039                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2040         }
2042         # Create hash for the answer
2043         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2044         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2046         # Call Opsi
2047         my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2048         if ($err){
2049                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2050         }
2051         # Add data to outgoing hash
2052         &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2053         &add_content2xml_hash($out_hash, "description", $res->{'description'});
2054         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2055         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2058         # Call Opsi two times
2059         my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2060         if ($usages_err){
2061                 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
2062         }
2063         my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2064         if ($licenses_err){
2065                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2066         }
2068         # Add data to outgoing hash
2069         # Parse through all software licenses and select those associated to the pool
2070         my $res_hash = { 'hit'=> [] };
2071         foreach my $license ( @$licenses_res) {
2072                 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2073                 my $found = 0;
2074                 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2075                 foreach my $lPI ( @licensePoolIds_list) {
2076                         if ($lPI eq $licensePoolId) { $found++ }
2077                 }
2078                 if (not $found ) { next; };
2079                 # Found matching licensePoolId
2080                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2081                         'licenseKeys' => {},
2082                         'expirationDate' => [$license->{'expirationDate'}],
2083                         'boundToHost' => [$license->{'boundToHost'}],
2084                         'maxInstallations' => [$license->{'maxInstallations'}],
2085                         'licenseType' => [$license->{'licenseType'}],
2086                         'licenseContractId' => [$license->{'licenseContractId'}],
2087                         'licensePoolIds' => [],
2088                         'hostIds' => [],
2089                         };
2090                 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2091                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2092                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2093                 }
2094                 foreach my $usage (@$usages_res) {
2095                         # Search for hostIds with matching softwareLicenseId
2096                         if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2097                                 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2098                         }
2099                 }
2101                 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
2102                 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
2103                 if ($lContract_err){
2104                         return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
2105                 }
2106                 $license_hash->{$license->{'licenseContractId'}} = [];
2107                 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
2108                         'notificationDate' => [$lContract_res->{notificationDate}],
2109                         'notes' => [$lContract_res->{notes}],
2110                         'exirationDate' => [$lContract_res->{expirationDate}],
2111                         'partner' => [$lContract_res->{partner}],
2112                 };
2113                 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2115                 push( @{$res_hash->{hit}}, $license_hash );
2116         }
2117         $out_hash->{licenses} = [$res_hash];
2119         my $endTime = Time::HiRes::time;
2120         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2121         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2122     return ( &create_xml_string($out_hash) );
2126 ################################
2127 # @brief Removes at first the software license from license pool and than deletes the software license. 
2128 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2129 # @param softwareLicenseId Identificator of a license.
2130 # @param licensePoolId The name of the pool.
2131 sub opsi_removeLicense {
2132         my $startTime = Time::HiRes::time;
2133     my ($msg, $msg_hash, $session_id) = @_;
2134     my $header = @{$msg_hash->{'header'}}[0];
2135     my $source = @{$msg_hash->{'source'}}[0];
2137         # Check input sanity
2138         my $softwareLicenseId;
2139         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2140                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2141         } else {
2142                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2143         }
2144         my $licensePoolId;
2145         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2146                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2147         } else {
2148                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2149         }
2150         
2151         # Call Opsi
2152         my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2153         if ($err){
2154                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2155         }
2157         # Call Opsi
2158         ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2159         if ($err){
2160                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2161         }
2163         # Create hash for the answer
2164         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2165         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2166         my $endTime = Time::HiRes::time;
2167         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2168         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2169         return ( &create_xml_string($out_hash) );
2173 ################################
2174 # @brief Return softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseContractId, expirationDate, boundToHost and a list of productIds.
2175 # @param hostId Something like client_1.intranet.mydomain.de
2176 sub opsi_getReservedLicenses {
2177         my $startTime = Time::HiRes::time;
2178         my ($msg, $msg_hash, $session_id) = @_;
2179         my $header = @{$msg_hash->{'header'}}[0];
2180         my $source = @{$msg_hash->{'source'}}[0];
2182         # Check input sanity
2183         my $hostId;
2184         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2185                 $hostId = @{$msg_hash->{'hostId'}}[0];
2186         } else {
2187                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2188         }
2190         # Fetch informations from Opsi server
2191         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2192         if ($license_err){
2193                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2194         }
2196         # Parse result
2197         my $res_hash = { 'hit'=> [] };
2198         foreach my $license ( @$license_res) {
2199                 if ($license->{boundToHost} ne $hostId) { next; }
2201                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2202                         'maxInstallations' => [$license->{'maxInstallations'}],
2203                         'boundToHost' => [$license->{'boundToHost'}],
2204                         'expirationDate' => [$license->{'expirationDate'}],
2205                         'licenseContractId' => [$license->{'licenseContractId'}],
2206                         'licenseType' => [$license->{'licenseType'}],
2207                         'licensePoolIds' => [],
2208                         };
2209                 
2210                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2211                         # Fetch information for license pools containing a software license which is bound to given host
2212                         my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2213                         if ($pool_err){
2214                                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2215                         }
2217                         # Add licensePool information to result hash
2218                         push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2219                         $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2220                         map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2221                         map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2222                 }
2223                 push( @{$res_hash->{hit}}, $license_hash );
2224         }
2225         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2226         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2227         $out_hash->{licenses} = [$res_hash];
2229         my $endTime = Time::HiRes::time;
2230         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2231         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2232     return ( &create_xml_string($out_hash) );
2235 ################################
2236 # @brief Bound the given softwareLicenseId to the given host.
2237 # @param hostId Opsi hostId
2238 # @param softwareLicenseId Identificator of a license (optional).
2239 sub opsi_boundHostToLicense {
2240         my $startTime = Time::HiRes::time;
2241         my ($msg, $msg_hash, $session_id) = @_;
2242         my $header = @{$msg_hash->{'header'}}[0];
2243         my $source = @{$msg_hash->{'source'}}[0];
2245         # Check input sanity
2246         my $hostId;
2247         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2248                 $hostId = @{$msg_hash->{'hostId'}}[0];
2249         } else {
2250                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2251         }
2252         my $softwareLicenseId;
2253         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2254                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2255         } else {
2256                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2257         }
2259         # Fetch informations from Opsi server
2260         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2261         if ($license_err){
2262                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2263         }
2265         # Memorize parameter for given softwareLicenseId
2266         my $licenseContractId;
2267         my $licenseType;
2268         my $maxInstallations;
2269         my $boundToHost;
2270         my $expirationDate = "";
2271         my $found;
2272         foreach my $license (@$license_res) {
2273                 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2274                 $licenseContractId = $license->{licenseContractId};
2275                 $licenseType = $license->{licenseType};
2276                 $maxInstallations = $license->{maxInstallations};
2277                 $expirationDate = $license->{expirationDate};
2278                 $found++;
2279         }
2281         if (not $found) {
2282                 return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
2283         }
2285         # Set boundToHost option for a given software license
2286         my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId, 
2287                         'licenseContractId' => $licenseContractId, 
2288                         'licenseType' => $licenseType, 
2289                         'maxInstallations' => $maxInstallations, 
2290                         'boundToHost' => $hostId, 
2291                         'expirationDate' => $expirationDate);
2292         if ($bound_err) {
2293                 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2294         }
2296         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2297         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2299         my $endTime = Time::HiRes::time;
2300         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2301         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2302     return ( &create_xml_string($out_hash) );
2305 ################################
2306 # @brief Release a software license formerly bound to a host.
2307 # @param softwareLicenseId Identificator of a license.
2308 sub opsi_unboundHostFromLicense {
2309         # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2310         # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2311         # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2312         my $startTime = Time::HiRes::time;
2313         my ($msg, $msg_hash, $session_id) = @_;
2314         my $header = @{$msg_hash->{'header'}}[0];
2315         my $source = @{$msg_hash->{'source'}}[0];
2317         # Check input sanity
2318         my $softwareLicenseId;
2319         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2320                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2321         } else {
2322                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2323         }
2324         
2325         # Memorize parameter witch are required for this procedure
2326         my $licenseContractId;
2327         my $licenseType;
2328         my $maxInstallations;
2329         my $expirationDate;
2330         my $partner;
2331         my $conclusionDate;
2332         my $notificationDate;
2333         my $notes;
2334         my $licensePoolId;
2335         my $licenseKey;
2337         # Fetch license informations from Opsi server
2338         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2339         if ($license_err){
2340                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2341         }
2342         my $found = 0;
2343         foreach my $license (@$license_res) {
2344                 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2345                 $licenseContractId = $license->{licenseContractId};
2346                 $licenseType = $license->{licenseType};
2347                 $maxInstallations = $license->{maxInstallations};
2348                 $expirationDate = $license->{expirationDate};
2349                 $licensePoolId = @{$license->{licensePoolIds}}[0];
2350                 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2351                 $found++;
2352         }
2353         
2354         # Fetch contract informations from Opsi server
2355         my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2356         if ($contract_err){
2357                 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2358         }
2359         $partner = $contract_res->{partner};
2360         $conclusionDate = $contract_res->{conclusionDate};
2361         $notificationDate = $contract_res->{notificationDate};
2362         $expirationDate = $contract_res->{expirationDate};
2363         $notes = $contract_res->{notes};
2365         # Delete software license
2366         my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2367         if ($err) {
2368                 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2369         }
2371         # Recreate software license without boundToHost
2372         ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate, 
2373                         'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2374         if ($err) {
2375                 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2376         }
2377         ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType, 
2378                         'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate       );
2379         if ($err) {
2380                 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2381         }
2382         ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2383         if ($err) {
2384                 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2385         }
2387         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2388         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2390         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2391     return ( &create_xml_string($out_hash) );
2394 ################################
2395 # @brief Returns a list of licenses with softwaerLicenseId, maxInstallations, boundToHost, expirationDate, licenseContractId, licenseType, a list of licensePoolIds with associated licenseKeys
2396 sub opsi_getAllSoftwareLicenses {
2397         my $startTime = Time::HiRes::time;
2398         my ($msg, $msg_hash, $session_id) = @_;
2399         my $header = @{$msg_hash->{'header'}}[0];
2400         my $source = @{$msg_hash->{'source'}}[0];
2402         my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
2403         if ($err) {
2404                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
2405         }
2407         # Parse result
2408         my $res_hash = { 'hit'=> [] };
2409         foreach my $license ( @$res) {
2410                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2411                         'maxInstallations' => [$license->{'maxInstallations'}],
2412                         'boundToHost' => [$license->{'boundToHost'}],
2413                         'expirationDate' => [$license->{'expirationDate'}],
2414                         'licenseContractId' => [$license->{'licenseContractId'}],
2415                         'licenseType' => [$license->{'licenseType'}],
2416                         'licensePoolIds' => [],
2417                         'licenseKeys'=> {}
2418                         };
2419                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2420                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2421                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2422                 }
2423                 push( @{$res_hash->{hit}}, $license_hash );
2424         }
2425         
2426         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2427         $out_hash->{licenses} = [$res_hash];
2428         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2430         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2431     return ( &create_xml_string($out_hash) );
2434 sub opsi_test {
2435     my ($msg, $msg_hash, $session_id) = @_;
2436     my $header = @{$msg_hash->{'header'}}[0];
2437     my $source = @{$msg_hash->{'source'}}[0];
2438         my $pram1 = @{$msg_hash->{'productId'}}[0];
2440 print STDERR Dumper $pram1;
2442         # Fetch infos from Opsi server
2443     my $callobj = {
2444         method  => 'getLicensePoolId',
2445         params  => [ $pram1 ],
2446         id  => 1,
2447     };
2448     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2450         print STDERR Dumper $res;
2451         return ();
2455 # ----------------------------------------------------------------------------
2456 #  internal methods handling the comunication with Opsi
2457 # ----------------------------------------------------------------------------
2459 ################################
2460 # @brief Checks if there is a specified tag and if the the tag has a content.
2461 sub _check_xml_tag_is_ok {
2462         my ($msg_hash,$tag) = @_;
2463         if (not defined $msg_hash->{$tag}) {
2464                 $_ = "message contains no tag '$tag'";
2465                 return 0;
2466         }
2467         if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
2468                 $_ = "message tag '$tag' has no content";
2469                 return  0;
2470         }
2471         return 1;
2474 ################################
2475 # @brief Writes the log line and returns the error message for GOsa.
2476 sub _giveErrorFeedback {
2477         my ($msg_hash, $err_string, $session_id) = @_;
2478         &main::daemon_log("$session_id ERROR: $err_string", 1);
2479         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2480     if (exists $msg_hash->{forward_to_gosa}) {
2481         &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
2482     }
2483         return ( &create_xml_string($out_hash) );
2487 ################################
2488 # @brief Perform the call to the Opsi server and measure the time for the call
2489 sub _callOpsi {
2490         my %arg = ('method'=>undef, 'params'=>[], 'id'=>1, @_);
2492         my $callObject = {
2493                 method => $arg{method},
2494                 params => $arg{params},
2495                 id => $arg{id},
2496         };
2498         my $startTime = Time::HiRes::time;
2499         my $opsiResult = $opsi_client->call($opsi_url, $callObject);
2500         my $endTime = Time::HiRes::time;
2501         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2503         &main::daemon_log("0 DEBUG: time to process opsi call '$arg{method}' : $elapsedTime seconds", 1034); 
2505         return $opsiResult;
2508 sub _getLicensePool_hash {
2509         my %arg = ( 'licensePoolId' => undef, @_ );
2511         if (not defined $arg{licensePoolId} ) { 
2512                 return ("function requires licensePoolId as parameter", 1);
2513         }
2515         my $res = &_callOpsi( method  => 'getLicensePool_hash', params =>[$arg{licensePoolId}], id  => 1 );
2516         my ($res_error, $res_error_str) = &check_opsi_res($res);
2517         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2519         return ($res->result, 0);
2522 sub _getSoftwareLicenses_listOfHashes {
2523         
2524         my $res = &_callOpsi( method  => 'getSoftwareLicenses_listOfHashes' );
2525         my ($res_error, $res_error_str) = &check_opsi_res($res);
2526         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2528         return ($res->result, 0);
2531 sub _getSoftwareLicenseUsages_listOfHashes {
2532         my %arg = ( 'hostId' => "", 'licensePoolId' => "", @_ );
2534         my $res = &_callOpsi( method=>'getSoftwareLicenseUsages_listOfHashes', params=>[ $arg{hostId}, $arg{licensePoolId} ] );
2535         my ($res_error, $res_error_str) = &check_opsi_res($res);
2536         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2538         return ($res->result, 0);
2541 sub _removeSoftwareLicenseFromLicensePool {
2542         my %arg = ( 'softwareLicenseId' => undef, 'licensePoolId' => undef, @_ );
2544         if (not defined $arg{softwareLicenseId} ) { 
2545                 return ("function requires softwareLicenseId as parameter", 1);
2546                 }
2547                 if (not defined $arg{licensePoolId} ) { 
2548                 return ("function requires licensePoolId as parameter", 1);
2549         }
2551         my $res = &_callOpsi( method=>'removeSoftwareLicenseFromLicensePool', params=>[ $arg{softwareLicenseId}, $arg{licensePoolId} ] );
2552         my ($res_error, $res_error_str) = &check_opsi_res($res);
2553         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2555         return ($res->result, 0);
2558 sub _deleteSoftwareLicense {
2559         my %arg = ( 'softwareLicenseId' => undef, 'removeFromPools' => "false", @_ );
2561         if (not defined $arg{softwareLicenseId} ) { 
2562                 return ("function requires softwareLicenseId as parameter", 1);
2563         }
2564         my $removeFromPools = "";
2565         if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) { 
2566                 $removeFromPools = "removeFromPools";
2567         }
2569         my $res = &_callOpsi( method=>'deleteSoftwareLicense', params=>[ $arg{softwareLicenseId}, $removeFromPools ] );
2570         my ($res_error, $res_error_str) = &check_opsi_res($res);
2571         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2573         return ($res->result, 0);
2576 sub _getLicensePoolId {
2577         my %arg = ( 'productId' => undef, @_ );
2578         
2579         if (not defined $arg{productId} ) {
2580                 return ("function requires productId as parameter", 1);
2581         }
2583     my $res = &_callOpsi( method  => 'getLicensePoolId', params  => [ $arg{productId} ] );
2584         my ($res_error, $res_error_str) = &check_opsi_res($res);
2585         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2587         return ($res->result, 0);
2590 sub _getLicenseContract_hash {
2591         my %arg = ( 'licenseContractId' => undef, @_ );
2592         
2593         if (not defined $arg{licenseContractId} ) {
2594                 return ("function requires licenseContractId as parameter", 1);
2595         }
2597     my $res = &_callOpsi( method  => 'getLicenseContract_hash', params  => [ $arg{licenseContractId} ] );
2598         my ($res_error, $res_error_str) = &check_opsi_res($res);
2599         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2601         return ($res->result, 0);
2604 sub _createLicenseContract {
2605         my %arg = (
2606                         'licenseContractId' => undef,
2607                         'partner' => undef,
2608                         'conclusionDate' => undef,
2609                         'notificationDate' => undef,
2610                         'expirationDate' => undef,
2611                         'notes' => undef,
2612                         @_ );
2614         my $res = &_callOpsi( method  => 'createLicenseContract', 
2615                         params  => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2616                         );
2617         my ($res_error, $res_error_str) = &check_opsi_res($res);
2618         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2620         return ($res->result, 0);
2623 sub _createSoftwareLicense {
2624         my %arg = (
2625                         'softwareLicenseId' => undef,
2626                         'licenseContractId' => undef,
2627                         'licenseType' => undef,
2628                         'maxInstallations' => undef,
2629                         'boundToHost' => undef,
2630                         'expirationDate' => undef,
2631                         @_ );
2633     my $res = &_callOpsi( method  => 'createSoftwareLicense',
2634         params  => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2635                 );
2636         my ($res_error, $res_error_str) = &check_opsi_res($res);
2637         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2639         return ($res->result, 0);
2642 sub _addSoftwareLicenseToLicensePool {
2643         my %arg = (
2644             'softwareLicenseId' => undef,
2645             'licensePoolId' => undef,
2646             'licenseKey' => undef,
2647             @_ );
2649         if (not defined $arg{softwareLicenseId} ) {
2650                 return ("function requires softwareLicenseId as parameter", 1);
2651         }
2652         if (not defined $arg{licensePoolId} ) {
2653                 return ("function requires licensePoolId as parameter", 1);
2654         }
2656         my $res = &_callOpsi( method  => 'addSoftwareLicenseToLicensePool', params  => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ] );
2657         my ($res_error, $res_error_str) = &check_opsi_res($res);
2658         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2660         return ($res->result, 0);
2663 sub _getProductStates_hash {
2664         my %arg = (     'hostId' => undef, @_ );
2666         if (not defined $arg{hostId} ) {
2667                 return ("function requires hostId as parameter", 1);
2668         }
2670         my $res = &_callOpsi( method => 'getProductStates_hash', params => [$arg{hostId}]);
2671         my ($res_error, $res_error_str) = &check_opsi_res($res);
2672         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2674         return ($res->result, 0);
2677 1;