Code

* add processing time measurement to all functions
[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);
51 use Time::HiRes qw( time );
53 BEGIN {}
55 END {}
57 # ----------------------------------------------------------------------------
58 #                          D E C L A R A T I O N S
59 # ----------------------------------------------------------------------------
61 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
65 # ----------------------------------------------------------------------------
66 #   external methods handling the comunication with GOsa/GOsa-si
67 # ----------------------------------------------------------------------------
69 ################################
70 # @brief A function returning a list of functions which are exported by importing the module.
71 # @return List of all provided functions
72 sub get_events {
73     return \@events;
74 }
76 ################################
77 # @brief Adds an Opsi product to an Opsi client.
78 # @param msg - STRING - xml message with tags hostId and productId
79 # @param msg_hash - HASHREF - message information parsed into a hash
80 # @param session_id - INTEGER - POE session id of the processing of this message
81 # @return out_msg - STRING - feedback to GOsa in success and error case
82 sub opsi_add_product_to_client {
83         my $startTime = Time::HiRes::time;
84     my ($msg, $msg_hash, $session_id) = @_;
85     my $header = @{$msg_hash->{'header'}}[0];
86     my $source = @{$msg_hash->{'source'}}[0];
87     my $target = @{$msg_hash->{'target'}}[0];
88     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
90     # Build return message
91     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
92     if (defined $forward_to_gosa) {
93         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
94     }
96     # Sanity check of needed parameter
97     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
98                 return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
99     }
100     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
101                 return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
102     }
104         # Get hostId
105         my $hostId = @{$msg_hash->{'hostId'}}[0];
106         &add_content2xml_hash($out_hash, "hostId", $hostId);
108         # Get productID
109         my $productId = @{$msg_hash->{'productId'}}[0];
110         &add_content2xml_hash($out_hash, "productId", $productId);
112         # Do an action request for all these -> "setup".
113         my $callobj = {
114                 method  => 'setProductActionRequest',
115                 params  => [ $productId, $hostId, "setup" ],
116                 id  => 1, }; 
117         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
119         if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
121         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
122     return ( &create_xml_string($out_hash) );
125 ################################
126 # @brief Deletes an Opsi-product from an Opsi-client. 
127 # @param msg - STRING - xml message with tags hostId and productId
128 # @param msg_hash - HASHREF - message information parsed into a hash
129 # @param session_id - INTEGER - POE session id of the processing of this message
130 # @return out_msg - STRING - feedback to GOsa in success and error case
131 sub opsi_del_product_from_client {
132         my $startTime = Time::HiRes::time;
133     my ($msg, $msg_hash, $session_id) = @_;
134     my $header = @{$msg_hash->{'header'}}[0];
135     my $source = @{$msg_hash->{'source'}}[0];
136     my $target = @{$msg_hash->{'target'}}[0];
137     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
138     my ($hostId, $productId);
139     my $error = 0;
140     my ($sres, $sres_err, $sres_err_string);
142     # Build return message
143     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
144     if (defined $forward_to_gosa) {
145         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
146     }
148     # Sanity check of needed parameter
149     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
150         $error++;
151         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
152         &add_content2xml_hash($out_hash, "error", "hostId");
153         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
155     }
156     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
157         $error++;
158         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
159         &add_content2xml_hash($out_hash, "error", "productId");
160         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
161     }
163     # All parameter available
164     if (not $error) {
165         # Get hostId
166         $hostId = @{$msg_hash->{'hostId'}}[0];
167         &add_content2xml_hash($out_hash, "hostId", $hostId);
169         # Get productID
170         $productId = @{$msg_hash->{'productId'}}[0];
171         &add_content2xml_hash($out_hash, "productId", $productId);
173         # Check to get product action list 
174         my $callobj = {
175             method  => 'getPossibleProductActions_list',
176             params  => [ $productId ],
177             id  => 1, };
178         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
179         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
180         if ($sres_err){
181             &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
182             &add_content2xml_hash($out_hash, "error", $sres_err_string);
183             $error++;
184         }
185     }
187     # Check action uninstall of product
188     if (not $error) {
189         my $uninst_possible= 0;
190         foreach my $r (@{$sres->result}) {
191             if ($r eq 'uninstall') {
192                 $uninst_possible= 1;
193             }
194         }
195         if (!$uninst_possible){
196             &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
197             &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
198             $error++;
199         }
200     }
202     # Set product state to "none"
203     # Do an action request for all these -> "setup".
204     if (not $error) {
205         my $callobj = {
206             method  => 'setProductActionRequest',
207             params  => [ $productId, $hostId, "none" ],
208             id  => 1, 
209         }; 
210         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
211         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
212         if ($sres_err){
213             &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
214             &add_content2xml_hash($out_hash, "error", $sres_err_string);
215         }
216     }
218     # Return message
219         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
220     return ( &create_xml_string($out_hash) );
223 ################################
224 # @brief Adds an Opsi client to Opsi.
225 # @param msg - STRING - xml message with tags hostId and macaddress
226 # @param msg_hash - HASHREF - message information parsed into a hash
227 # @param session_id - INTEGER - POE session id of the processing of this message
228 # @return out_msg - STRING - feedback to GOsa in success and error case
229 sub opsi_add_client {
230         my $startTime = Time::HiRes::time;
231     my ($msg, $msg_hash, $session_id) = @_;
232     my $header = @{$msg_hash->{'header'}}[0];
233     my $source = @{$msg_hash->{'source'}}[0];
234     my $target = @{$msg_hash->{'target'}}[0];
235     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
236     my ($hostId, $mac);
237     my $error = 0;
238     my ($sres, $sres_err, $sres_err_string);
240     # Build return message with twisted target and source
241     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
242     if (defined $forward_to_gosa) {
243         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
244     }
246     # Sanity check of needed parameter
247     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
248         $error++;
249         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
250         &add_content2xml_hash($out_hash, "error", "hostId");
251         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
252     }
253     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
254         $error++;
255         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
256         &add_content2xml_hash($out_hash, "error", "macaddress");
257         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
258     }
260     if (not $error) {
261         # Get hostId
262         $hostId = @{$msg_hash->{'hostId'}}[0];
263         &add_content2xml_hash($out_hash, "hostId", $hostId);
265         # Get macaddress
266         $mac = @{$msg_hash->{'macaddress'}}[0];
267         &add_content2xml_hash($out_hash, "macaddress", $mac);
269         my $name= $hostId;
270         $name=~ s/^([^.]+).*$/$1/;
271         my $domain= $hostId;
272         $domain=~ s/^[^.]+\.(.*)$/$1/;
273         my ($description, $notes, $ip);
275         if (defined @{$msg_hash->{'description'}}[0]){
276             $description = @{$msg_hash->{'description'}}[0];
277         }
278         if (defined @{$msg_hash->{'notes'}}[0]){
279             $notes = @{$msg_hash->{'notes'}}[0];
280         }
281         if (defined @{$msg_hash->{'ip'}}[0]){
282             $ip = @{$msg_hash->{'ip'}}[0];
283         }
285         my $callobj;
286         $callobj = {
287             method  => 'createClient',
288             params  => [ $name, $domain, $description, $notes, $ip, $mac ],
289             id  => 1,
290         };
292         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
293         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
294         if ($sres_err){
295             &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
296             &add_content2xml_hash($out_hash, "error", $sres_err_string);
297         } else {
298             &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5); 
299         }
300     }
302     # Return message
303         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
304     return ( &create_xml_string($out_hash) );
307 ################################
308 # @brief Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
309 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
310 # @param msg_hash - HASHREF - message information parsed into a hash
311 # @param session_id - INTEGER - POE session id of the processing of this message    
312 # @return out_msg - STRING - feedback to GOsa in success and error case
313 sub opsi_modify_client {
314         my $startTime = Time::HiRes::time;
315     my ($msg, $msg_hash, $session_id) = @_;
316     my $header = @{$msg_hash->{'header'}}[0];
317     my $source = @{$msg_hash->{'source'}}[0];
318     my $target = @{$msg_hash->{'target'}}[0];
319     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
320     my $hostId;
321     my $error = 0;
322     my ($sres, $sres_err, $sres_err_string);
324     # Build return message with twisted target and source
325     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
326     if (defined $forward_to_gosa) {
327         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
328     }
330     # Sanity check of needed parameter
331     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
332         $error++;
333         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
334         &add_content2xml_hash($out_hash, "error", "hostId");
335         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
336     }
338     if (not $error) {
339         # Get hostId
340         $hostId = @{$msg_hash->{'hostId'}}[0];
341         &add_content2xml_hash($out_hash, "hostId", $hostId);
342         my $name= $hostId;
343         $name=~ s/^([^.]+).*$/$1/;
344         my $domain= $hostId;
345         $domain=~ s/^[^.]+(.*)$/$1/;
347         # Modify description, notes or mac if defined
348         my ($description, $notes, $mac);
349         my $callobj;
350         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
351             $description = @{$msg_hash->{'description'}}[0];
352             $callobj = {
353                 method  => 'setHostDescription',
354                 params  => [ $hostId, $description ],
355                 id  => 1,
356             };
357             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
358             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
359             if ($sres_err){
360                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
361                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
362             }
363         }
364         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
365             $notes = @{$msg_hash->{'notes'}}[0];
366             $callobj = {
367                 method  => 'setHostNotes',
368                 params  => [ $hostId, $notes ],
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 notes: ".$sres_err_string, 1);
375                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
376             }
377         }
378         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
379             $mac = @{$msg_hash->{'mac'}}[0];
380             $callobj = {
381                 method  => 'setMacAddress',
382                 params  => [ $hostId, $mac ],
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 mac address: ".$sres_err_string, 1);
389                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
390             }
391         }
392     }
394     # Return message
395         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
396     return ( &create_xml_string($out_hash) );
398  
399 ################################
400 # @brief Get netboot products for specific host.
401 # @param msg - STRING - xml message with tag hostId
402 # @param msg_hash - HASHREF - message information parsed into a hash
403 # @param session_id - INTEGER - POE session id of the processing of this message
404 # @return out_msg - STRING - feedback to GOsa in success and error case
405 sub opsi_get_netboot_products {
406         my $startTime = Time::HiRes::time;
407     my ($msg, $msg_hash, $session_id) = @_;
408     my $header = @{$msg_hash->{'header'}}[0];
409     my $source = @{$msg_hash->{'source'}}[0];
410     my $target = @{$msg_hash->{'target'}}[0];
411     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
412     my $hostId;
413     my $xml_msg;
415     # Build return message with twisted target and source
416     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
417     if (defined $forward_to_gosa) {
418         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
419     }
421     # Get hostId if defined
422     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
423         $hostId = @{$msg_hash->{'hostId'}}[0];
424         &add_content2xml_hash($out_hash, "hostId", $hostId);
425     }
427     &add_content2xml_hash($out_hash, "xxx", "");
428     $xml_msg = &create_xml_string($out_hash);
429     # For hosts, only return the products that are or get installed
430     my $callobj;
431     $callobj = {
432         method  => 'getNetBootProductIds_list',
433         params  => [ ],
434         id  => 1,
435     };
436     &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
437     &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
438     &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
439     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
440     &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
441     my %r = ();
442     for (@{$res->result}) { $r{$_} = 1 }
444       if (not &check_opsi_res($res)){
446         if (defined $hostId){
448             $callobj = {
449                 method  => 'getProductStates_hash',
450                 params  => [ $hostId ],
451                 id  => 1,
452             };
454             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
455             if (not &check_opsi_res($hres)){
456                 my $htmp= $hres->result->{$hostId};
458                 # check state != not_installed or action == setup -> load and add
459                 foreach my $product (@{$htmp}){
461                     if (!defined ($r{$product->{'productId'}})){
462                         next;
463                     }
465                     # Now we've a couple of hashes...
466                     if ($product->{'installationStatus'} ne "not_installed" or
467                             $product->{'actionRequest'} eq "setup"){
468                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
470                         $callobj = {
471                             method  => 'getProduct_hash',
472                             params  => [ $product->{'productId'} ],
473                             id  => 1,
474                         };
476                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
477                         if (not &check_opsi_res($sres)){
478                             my $tres= $sres->result;
480                             my $name= xml_quote($tres->{'name'});
481                             my $r= $product->{'productId'};
482                             my $description= xml_quote($tres->{'description'});
483                             $name=~ s/\//\\\//;
484                             $description=~ s/\//\\\//;
485                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
486                         }
487                     }
488                 }
490             }
492         } else {
493             foreach my $r (@{$res->result}) {
494                 $callobj = {
495                     method  => 'getProduct_hash',
496                     params  => [ $r ],
497                     id  => 1,
498                 };
500                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
501                 if (not &check_opsi_res($sres)){
502                     my $tres= $sres->result;
504                     my $name= xml_quote($tres->{'name'});
505                     my $description= xml_quote($tres->{'description'});
506                     $name=~ s/\//\\\//;
507                     $description=~ s/\//\\\//;
508                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
509                 }
510             }
512         }
513     }
514     $xml_msg=~ s/<xxx><\/xxx>//;
516     # Return message
517         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
518     return ( $xml_msg );
521 ################################   
522 # @brief Get product properties for a product and a specific host or gobally for a product.
523 # @param msg - STRING - xml message with tags productId and optional hostId
524 # @param msg_hash - HASHREF - message information parsed into a hash
525 # @param session_id - INTEGER - POE session id of the processing of this message
526 # @return out_msg - STRING - feedback to GOsa in success and error case
527 sub opsi_get_product_properties {
528         my $startTime = Time::HiRes::time;
529     my ($msg, $msg_hash, $session_id) = @_;
530     my $header = @{$msg_hash->{'header'}}[0];
531     my $source = @{$msg_hash->{'source'}}[0];
532     my $target = @{$msg_hash->{'target'}}[0];
533     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
534     my ($hostId, $productId);
535     my $xml_msg;
537     # Build return message with twisted target and source
538     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
539     if (defined $forward_to_gosa) {
540         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
541     }
543     # Sanity check of needed parameter
544     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
545         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
546         &add_content2xml_hash($out_hash, "error", "productId");
547         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
549         # Return message
550         return ( &create_xml_string($out_hash) );
551     }
553     # Get productid
554     $productId = @{$msg_hash->{'productId'}}[0];
555     &add_content2xml_hash($out_hash, "producId", "$productId");
557     # Get hostId if defined
558     if (defined @{$msg_hash->{'hostId'}}[0]){
559       $hostId = @{$msg_hash->{'hostId'}}[0];
560       &add_content2xml_hash($out_hash, "hostId", $hostId);
561     }
563     # Load actions
564     my $callobj = {
565       method  => 'getPossibleProductActions_list',
566       params  => [ $productId ],
567       id  => 1,
568     };
569     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
570     if (not &check_opsi_res($res)){
571       foreach my $action (@{$res->result}){
572         &add_content2xml_hash($out_hash, "action", $action);
573       }
574     }
576     # Add place holder
577     &add_content2xml_hash($out_hash, "xxx", "");
579     # Move to XML string
580     $xml_msg= &create_xml_string($out_hash);
582     # JSON Query
583     if (defined $hostId){
584       $callobj = {
585           method  => 'getProductProperties_hash',
586           params  => [ $productId, $hostId ],
587           id  => 1,
588       };
589     } else {
590       $callobj = {
591           method  => 'getProductProperties_hash',
592           params  => [ $productId ],
593           id  => 1,
594       };
595     }
596     $res = $main::opsi_client->call($main::opsi_url, $callobj);
598     # JSON Query 2
599     $callobj = {
600       method  => 'getProductPropertyDefinitions_listOfHashes',
601       params  => [ $productId ],
602       id  => 1,
603     };
605     # Assemble options
606     my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
607     my $values = {};
608     my $descriptions = {};
609     if (not &check_opsi_res($res2)){
610         my $r= $res2->result;
612           foreach my $entr (@$r){
613             # Unroll values
614             my $cnv;
615             if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
616               foreach my $v (@{$entr->{'values'}}){
617                 $cnv.= "<value>$v</value>";
618               }
619             } else {
620               $cnv= $entr->{'values'};
621             }
622             $values->{$entr->{'name'}}= $cnv;
623             $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
624           }
625     }
627     if (not &check_opsi_res($res)){
628         my $r= $res->result;
629         foreach my $key (keys %{$r}) {
630             my $item= "\n<item>";
631             my $value= $r->{$key};
632             my $dsc= "";
633             my $vals= "";
634             if (defined $descriptions->{$key}){
635               $dsc= $descriptions->{$key};
636             }
637             if (defined $values->{$key}){
638               $vals= $values->{$key};
639             }
640             $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
641             $item.= "</item>";
642             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
643         }
644     }
646     $xml_msg=~ s/<xxx><\/xxx>//;
648     # Return message
649         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
650     return ( $xml_msg );
653 ################################   
654 # @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.
655 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
656 # @param msg_hash - HASHREF - message information parsed into a hash
657 # @param session_id - INTEGER - POE session id of the processing of this message
658 # @return out_msg - STRING - feedback to GOsa in success and error case
659 sub opsi_set_product_properties {
660         my $startTime = Time::HiRes::time;
661     my ($msg, $msg_hash, $session_id) = @_;
662     my $header = @{$msg_hash->{'header'}}[0];
663     my $source = @{$msg_hash->{'source'}}[0];
664     my $target = @{$msg_hash->{'target'}}[0];
665     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
666     my ($productId, $hostId);
668     # Build return message with twisted target and source
669     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
670     if (defined $forward_to_gosa) {
671         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
672     }
674     # Sanity check of needed parameter
675     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
676         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
677         &add_content2xml_hash($out_hash, "error", "productId");
678         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
679         return ( &create_xml_string($out_hash) );
680     }
681     if (not exists $msg_hash->{'item'}) {
682         &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
683         &add_content2xml_hash($out_hash, "error", "item");
684         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
685         return ( &create_xml_string($out_hash) );
686     } else {
687         if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
688             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
689             &add_content2xml_hash($out_hash, "error", "name");
690             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
691             return ( &create_xml_string($out_hash) );
692         }
693         if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
694             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
695             &add_content2xml_hash($out_hash, "error", "value");
696             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
697             return ( &create_xml_string($out_hash) );
698         }
699     }
700     # if no hostId is given, set_product_properties will act on globally
701     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
702         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
703         &add_content2xml_hash($out_hash, "error", "hostId");
704         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
705         return ( &create_xml_string($out_hash) );
706     }
708         
709     # Get productId
710     $productId =  @{$msg_hash->{'productId'}}[0];
711     &add_content2xml_hash($out_hash, "productId", $productId);
713     # Get hostId if defined
714     if (exists $msg_hash->{'hostId'}){
715         $hostId = @{$msg_hash->{'hostId'}}[0];
716         &add_content2xml_hash($out_hash, "hostId", $hostId);
717     }
719     # Set product states if requested
720     if (defined @{$msg_hash->{'action'}}[0]){
721         &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
722     }
723     if (defined @{$msg_hash->{'state'}}[0]){
724         &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
725     }
727     # Find properties
728     foreach my $item (@{$msg_hash->{'item'}}){
729         # JSON Query
730         my $callobj;
732         if (defined $hostId){
733             $callobj = {
734                 method  => 'setProductProperty',
735                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
736                 id  => 1,
737             };
738         } else {
739             $callobj = {
740                 method  => 'setProductProperty',
741                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
742                 id  => 1,
743             };
744         }
746         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
747         my ($res_err, $res_err_string) = &check_opsi_res($res);
749         if ($res_err){
750             &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
751             &add_content2xml_hash($out_hash, "error", $res_err_string);
752         }
753     }
756     # Return message
757         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
758     return ( &create_xml_string($out_hash) );
761 ################################   
762 # @brief Reports client hardware inventory.
763 # @param msg - STRING - xml message with tag hostId
764 # @param msg_hash - HASHREF - message information parsed into a hash
765 # @param session_id - INTEGER - POE session id of the processing of this message
766 # @return out_msg - STRING - feedback to GOsa in success and error case
767 sub opsi_get_client_hardware {
768         my $startTime = Time::HiRes::time;
769     my ($msg, $msg_hash, $session_id) = @_;
770     my $header = @{$msg_hash->{'header'}}[0];
771     my $source = @{$msg_hash->{'source'}}[0];
772     my $target = @{$msg_hash->{'target'}}[0];
773     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
774     my $hostId;
775     my $error = 0;
776     my $xml_msg;
778     # Sanity check of needed parameter
779         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
780         $hostId = @{$msg_hash->{'hostId'}}[0];
781         } else {
782                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
783         }
786     # Build return message with twisted target and source
787     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
788     if (defined $forward_to_gosa) {
789       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
790     }
791         &add_content2xml_hash($out_hash, "hostId", "$hostId");
792         &add_content2xml_hash($out_hash, "xxx", "");
794     # Move to XML string
795     $xml_msg= &create_xml_string($out_hash);
796     
797         my $res = &_callOpsi(method=>'getHardwareInformation_hash', params=>[ $hostId ]);
798         if (not &check_opsi_res($res)){
799                 my $result= $res->result;
800                 if (ref $result eq "HASH") {
801                         foreach my $r (keys %{$result}){
802                                 my $item= "\n<item><id>".xml_quote($r)."</id>";
803                                 my $value= $result->{$r};
804                                 foreach my $sres (@{$value}){
806                                         foreach my $dres (keys %{$sres}){
807                                                 if (defined $sres->{$dres}){
808                                                         $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
809                                                 }
810                                         }
812                                 }
813                                 $item.= "</item>";
814                                 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
816                         }
817                 }
818         }
820         $xml_msg=~ s/<xxx><\/xxx>//;
822     # Return message
823         my $endTime = Time::HiRes::time;
824         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
825         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
826     return ( $xml_msg );
829 ################################   
830 # @brief Reports all Opsi clients. 
831 # @param msg - STRING - xml message 
832 # @param msg_hash - HASHREF - message information parsed into a hash
833 # @param session_id - INTEGER - POE session id of the processing of this message
834 # @return out_msg - STRING - feedback to GOsa in success and error case
835 sub opsi_list_clients {
836         my $startTime = Time::HiRes::time;
837     my ($msg, $msg_hash, $session_id) = @_;
838     my $header = @{$msg_hash->{'header'}}[0];
839     my $source = @{$msg_hash->{'source'}}[0];
840     my $target = @{$msg_hash->{'target'}}[0];
841     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
843     # Build return message with twisted target and source
844     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
845     if (defined $forward_to_gosa) {
846       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
847     }
848     &add_content2xml_hash($out_hash, "xxx", "");
850     # Move to XML string
851     my $xml_msg= &create_xml_string($out_hash);
853     # JSON Query
854     my $callobj = {
855         method  => 'getClients_listOfHashes',
856         params  => [ ],
857         id  => 1,
858     };
859     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
860     if (not &check_opsi_res($res)){
861         foreach my $host (@{$res->result}){
862             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
863             if (defined($host->{'description'})){
864                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
865             }
866             if (defined($host->{'notes'})){
867                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
868             }
869             if (defined($host->{'lastSeen'})){
870                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
871             }
873             $callobj = {
874               method  => 'getIpAddress',
875               params  => [ $host->{'hostId'} ],
876               id  => 1,
877             };
878             my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
879             if ( not &check_opsi_res($sres)){
880               $item.= "<ip>".xml_quote($sres->result)."</ip>";
881             }
883             $callobj = {
884               method  => 'getMacAddress',
885               params  => [ $host->{'hostId'} ],
886               id  => 1,
887             };
888             $sres= $main::opsi_client->call($main::opsi_url, $callobj);
889             if ( not &check_opsi_res($sres)){
890                 $item.= "<mac>".xml_quote($sres->result)."</mac>";
891             }
892             $item.= "</item>";
893             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
894         }
895     }
896     $xml_msg=~ s/<xxx><\/xxx>//;
898         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
899     return ( $xml_msg );
902 ################################   
903 # @brief Reports client software inventory.
904 # @param msg - STRING - xml message with tag hostId
905 # @param msg_hash - HASHREF - message information parsed into a hash
906 # @param session_id - INTEGER - POE session id of the processing of this message
907 # @return out_msg - STRING - feedback to GOsa in success and error case
908 sub opsi_get_client_software {
909         my $startTime = Time::HiRes::time;
910     my ($msg, $msg_hash, $session_id) = @_;
911     my $header = @{$msg_hash->{'header'}}[0];
912     my $source = @{$msg_hash->{'source'}}[0];
913     my $target = @{$msg_hash->{'target'}}[0];
914     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
915     my $error = 0;
916     my $hostId;
917     my $xml_msg;
919     # Build return message with twisted target and source
920     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
921     if (defined $forward_to_gosa) {
922       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
923     }
925     # Sanity check of needed parameter
926     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
927         $error++;
928         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
929         &add_content2xml_hash($out_hash, "error", "hostId");
930         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
931     }
933     if (not $error) {
935     # Get hostId
936         $hostId = @{$msg_hash->{'hostId'}}[0];
937         &add_content2xml_hash($out_hash, "hostId", "$hostId");
938         &add_content2xml_hash($out_hash, "xxx", "");
939     }
941     $xml_msg= &create_xml_string($out_hash);
943     if (not $error) {
945     # JSON Query
946         my $callobj = {
947             method  => 'getSoftwareInformation_hash',
948             params  => [ $hostId ],
949             id  => 1,
950         };
952         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
953         if (not &check_opsi_res($res)){
954             my $result= $res->result;
955         }
957         $xml_msg=~ s/<xxx><\/xxx>//;
959     }
961     # Return message
962         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
963     return ( $xml_msg );
966 ################################   
967 # @brief Reports product for given hostId or globally.
968 # @param msg - STRING - xml message with optional tag hostId
969 # @param msg_hash - HASHREF - message information parsed into a hash
970 # @param session_id - INTEGER - POE session id of the processing of this message
971 # @return out_msg - STRING - feedback to GOsa in success and error case
972 sub opsi_get_local_products {
973         my $startTime = Time::HiRes::time;
974     my ($msg, $msg_hash, $session_id) = @_;
975     my $header = @{$msg_hash->{'header'}}[0];
976     my $source = @{$msg_hash->{'source'}}[0];
977     my $target = @{$msg_hash->{'target'}}[0];
978     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
979     my $hostId;
981     # Build return message with twisted target and source
982     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
983     if (defined $forward_to_gosa) {
984         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
985     }
986     &add_content2xml_hash($out_hash, "xxx", "");
988     # Get hostId if defined
989     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
990         $hostId = @{$msg_hash->{'hostId'}}[0];
991         &add_content2xml_hash($out_hash, "hostId", $hostId);
992     }
994     # Move to XML string
995     my $xml_msg= &create_xml_string($out_hash);
997     # For hosts, only return the products that are or get installed
998     my $callobj;
999     $callobj = {
1000         method  => 'getLocalBootProductIds_list',
1001         params  => [ ],
1002         id  => 1,
1003     };
1005     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1006     my %r = ();
1007     for (@{$res->result}) { $r{$_} = 1 }
1009     if (not &check_opsi_res($res)){
1011         if (defined $hostId){
1012             $callobj = {
1013                 method  => 'getProductStates_hash',
1014                 params  => [ $hostId ],
1015                 id  => 1,
1016             };
1018             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1019             if (not &check_opsi_res($hres)){
1020                 my $htmp= $hres->result->{$hostId};
1022                 # Check state != not_installed or action == setup -> load and add
1023                 foreach my $product (@{$htmp}){
1025                     if (!defined ($r{$product->{'productId'}})){
1026                         next;
1027                     }
1029                     # Now we've a couple of hashes...
1030                     if ($product->{'installationStatus'} ne "not_installed" or
1031                             $product->{'actionRequest'} eq "setup"){
1032                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1034                         $callobj = {
1035                             method  => 'getProduct_hash',
1036                             params  => [ $product->{'productId'} ],
1037                             id  => 1,
1038                         };
1040                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1041                         if (not &check_opsi_res($sres)){
1042                             my $tres= $sres->result;
1044                             my $name= xml_quote($tres->{'name'});
1045                             my $r= $product->{'productId'};
1046                             my $description= xml_quote($tres->{'description'});
1047                             $name=~ s/\//\\\//;
1048                             $description=~ s/\//\\\//;
1049                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1050                         }
1052                     }
1053                 }
1055             }
1057         } else {
1058             foreach my $r (@{$res->result}) {
1059                 $callobj = {
1060                     method  => 'getProduct_hash',
1061                     params  => [ $r ],
1062                     id  => 1,
1063                 };
1065                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1066                 if (not &check_opsi_res($sres)){
1067                     my $tres= $sres->result;
1069                     my $name= xml_quote($tres->{'name'});
1070                     my $description= xml_quote($tres->{'description'});
1071                     $name=~ s/\//\\\//;
1072                     $description=~ s/\//\\\//;
1073                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1074                 }
1076             }
1078         }
1079     }
1081     $xml_msg=~ s/<xxx><\/xxx>//;
1083     # Retrun Message
1084         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1085     return ( $xml_msg );
1088 ################################   
1089 # @brief Deletes a client from Opsi.
1090 # @param msg - STRING - xml message with tag hostId
1091 # @param msg_hash - HASHREF - message information parsed into a hash
1092 # @param session_id - INTEGER - POE session id of the processing of this message
1093 # @return out_msg - STRING - feedback to GOsa in success and error case
1094 sub opsi_del_client {
1095         my $startTime = Time::HiRes::time;
1096     my ($msg, $msg_hash, $session_id) = @_;
1097     my $header = @{$msg_hash->{'header'}}[0];
1098     my $source = @{$msg_hash->{'source'}}[0];
1099     my $target = @{$msg_hash->{'target'}}[0];
1100     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1101     my $hostId;
1102     my $error = 0;
1104     # Build return message with twisted target and source
1105     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1106     if (defined $forward_to_gosa) {
1107       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1108     }
1110     # Sanity check of needed parameter
1111     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1112         $error++;
1113         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1114         &add_content2xml_hash($out_hash, "error", "hostId");
1115         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1116     }
1118     if (not $error) {
1120     # Get hostId
1121         $hostId = @{$msg_hash->{'hostId'}}[0];
1122         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1124     # JSON Query
1125         my $callobj = {
1126             method  => 'deleteClient',
1127             params  => [ $hostId ],
1128             id  => 1,
1129         };
1130         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1131     }
1133     # Move to XML string
1134     my $xml_msg= &create_xml_string($out_hash);
1136     # Return message
1137         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1138     return ( $xml_msg );
1141 ################################   
1142 # @brief Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1143 # @param msg - STRING - xml message with tags hostId, macaddress
1144 # @param msg_hash - HASHREF - message information parsed into a hash
1145 # @param session_id - INTEGER - POE session id of the processing of this message
1146 # @return out_msg - STRING - feedback to GOsa in success and error case
1147 sub opsi_install_client {
1148         my $startTime = Time::HiRes::time;
1149     my ($msg, $msg_hash, $session_id) = @_;
1150     my $header = @{$msg_hash->{'header'}}[0];
1151     my $source = @{$msg_hash->{'source'}}[0];
1152     my $target = @{$msg_hash->{'target'}}[0];
1153     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1154     my ($hostId, $macaddress);
1155     my $error = 0;
1156     my @out_msg_l;
1158     # Build return message with twisted target and source
1159     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1160     if (defined $forward_to_gosa) {
1161         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1162     }
1164     # Sanity check of needed parameter
1165     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1166         $error++;
1167         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1168         &add_content2xml_hash($out_hash, "error", "hostId");
1169         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1170     }
1171     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
1172         $error++;
1173         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1174         &add_content2xml_hash($out_hash, "error", "macaddress");
1175         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1176     } else {
1177         if ((exists $msg_hash->{'macaddress'}) && 
1178                 ($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)) {  
1179             $macaddress = $1; 
1180         } else { 
1181             $error ++; 
1182             &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1183             &add_content2xml_hash($out_hash, "error", "macaddress");
1184             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1185         }
1186     }
1188     if (not $error) {
1190     # Get hostId
1191         $hostId = @{$msg_hash->{'hostId'}}[0];
1192         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1194         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1195         my $callobj = {
1196             method  => 'getProductStates_hash',
1197             params  => [ $hostId ],
1198             id  => 1,
1199         };
1201         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1202         if (not &check_opsi_res($hres)){
1203             my $htmp= $hres->result->{$hostId};
1205             # check state != not_installed or action == setup -> load and add
1206             foreach my $product (@{$htmp}){
1207                 # Now we've a couple of hashes...
1208                 if ($product->{'installationStatus'} ne "not_installed" or
1209                         $product->{'actionRequest'} ne "none"){
1211                     # Do an action request for all these -> "setup".
1212                     $callobj = {
1213                         method  => 'setProductActionRequest',
1214                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1215                         id  => 1,
1216                     };
1217                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1218                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1219                     if ($res_err){
1220                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1221                     } else {
1222                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1223                     }
1224                 }
1225             }
1226         }
1227         push(@out_msg_l, &create_xml_string($out_hash));
1228     
1230     # Build wakeup message for client
1231         if (not $error) {
1232             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1233             &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1234             my $wakeup_msg = &create_xml_string($wakeup_hash);
1235             push(@out_msg_l, $wakeup_msg);
1237             # invoke trigger wake for this gosa-si-server
1238             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1239         }
1240     }
1241     
1242     # Return messages
1243         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1244     return @out_msg_l;
1247 ################################
1248 # @brief Set action for an Opsi client
1249 # @param product - STRING - Opsi product
1250 # @param action - STRING - action
1251 # @param hostId - STRING - Opsi hostId
1252 sub _set_action {
1253   my $product= shift;
1254   my $action = shift;
1255   my $hostId = shift;
1256   my $callobj;
1258   $callobj = {
1259     method  => 'setProductActionRequest',
1260     params  => [ $product, $hostId, $action],
1261     id  => 1,
1262   };
1264   $main::opsi_client->call($main::opsi_url, $callobj);
1267 ################################
1268 # @brief Set state for an Opsi client
1269 # @param product - STRING - Opsi product
1270 # @param action - STRING - state
1271 # @param hostId - STRING - Opsi hostId
1272 sub _set_state {
1273   my $product = shift;
1274   my $state = shift;
1275   my $hostId = shift;
1276   my $callobj;
1278   $callobj = {
1279     method  => 'setProductState',
1280     params  => [ $product, $hostId, $state ],
1281     id  => 1,
1282   };
1284   $main::opsi_client->call($main::opsi_url, $callobj);
1287 ################################
1288 # @brief Create a license pool at Opsi server.
1289 # @param licensePoolId The name of the pool (optional). 
1290 # @param description The description of the pool (optional).
1291 # @param productIds A list of assigned porducts of the pool (optional). 
1292 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional). 
1293 sub opsi_createLicensePool {
1294         my $startTime = Time::HiRes::time;
1295     my ($msg, $msg_hash, $session_id) = @_;
1296     my $header = @{$msg_hash->{'header'}}[0];
1297     my $source = @{$msg_hash->{'source'}}[0];
1298     my $target = @{$msg_hash->{'target'}}[0];
1299         my $out_hash;
1300         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1301         my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1302         my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1303         my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1305         # Create license Pool
1306     my $callobj = {
1307         method  => 'createLicensePool',
1308         params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1309         id  => 1,
1310     };
1311     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1313         # Check Opsi error
1314         my ($res_error, $res_error_str) = &check_opsi_res($res);
1315         if ($res_error){
1316                 # Create error message
1317                 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1318                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1319                 return ( &create_xml_string($out_hash) );
1320         }
1322         # Create function result message
1323         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1324         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1326         my $endTime = Time::HiRes::time;
1327         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1328         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1329         return ( &create_xml_string($out_hash) );
1332 ################################
1333 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1334 sub opsi_getLicensePools_listOfHashes {
1335         my $startTime = Time::HiRes::time;
1336     my ($msg, $msg_hash, $session_id) = @_;
1337     my $header = @{$msg_hash->{'header'}}[0];
1338     my $source = @{$msg_hash->{'source'}}[0];
1339         my $out_hash;
1341         # Fetch infos from Opsi server
1342     my $callobj = {
1343         method  => 'getLicensePools_listOfHashes',
1344         params  => [ ],
1345         id  => 1,
1346     };
1347     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1349         # Check Opsi error
1350         my ($res_error, $res_error_str) = &check_opsi_res($res);
1351         if ($res_error){
1352                 # Create error message
1353                 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1354                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1355                 return ( &create_xml_string($out_hash) );
1356         }
1358         # Create function result message
1359         my $res_hash = { 'hit'=> [] };
1360         foreach my $licensePool ( @{$res->result}) {
1361                 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1362                         'description' => [$licensePool->{'description'}],
1363                         'productIds' => $licensePool->{'productIds'},
1364                         'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1365                         };
1366                 push( @{$res_hash->{hit}}, $licensePool_hash );
1367         }
1369         # Create function result message
1370         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1371         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1372         $out_hash->{result} = [$res_hash];
1374         my $endTime = Time::HiRes::time;
1375         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1376         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1377         return ( &create_xml_string($out_hash) );
1380 ################################
1381 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1382 # @param licensePoolId The name of the pool. 
1383 sub opsi_getLicensePool_hash {
1384         my $startTime = Time::HiRes::time;
1385     my ($msg, $msg_hash, $session_id) = @_;
1386     my $header = @{$msg_hash->{'header'}}[0];
1387     my $source = @{$msg_hash->{'source'}}[0];
1388     my $target = @{$msg_hash->{'target'}}[0];
1389     my $licensePoolId;
1390         my $out_hash;
1392         # Check input sanity
1393         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1394                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1395         } else {
1396                 return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
1397         }
1399         # Fetch infos from Opsi server
1400     my $callobj = {
1401         method  => 'getLicensePool_hash',
1402         params  => [ $licensePoolId ],
1403         id  => 1,
1404     };
1405     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1407         # Check Opsi error
1408         my ($res_error, $res_error_str) = &check_opsi_res($res);
1409         if ($res_error){
1410                 # Create error message
1411                 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1412                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1413                 &add_content2xml_hash($out_hash, "error", $res_error_str);
1414                 return ( &create_xml_string($out_hash) );
1415         }
1417         # Create function result message
1418         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1419         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1420         &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1421         &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1422         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1423         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1425         my $endTime = Time::HiRes::time;
1426         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1427         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1428         return ( &create_xml_string($out_hash) );
1431 sub _parse_getSoftwareLicenseUsages {
1432         my $res = shift;
1434         # Parse Opsi result
1435         my $tmp_licensePool_cache = {};
1436         my $res_hash = { 'hit'=> [] };
1437         foreach my $license ( @{$res}) {
1438                 my $tmp_licensePool = $license->{'licensePoolId'};
1439                 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1440                         # Fetch missing informations from Opsi and cache the results for a possible later usage
1441                         my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1442                         if (not $err) {
1443                                 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1444                         }
1445                 }
1446                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1447                         'notes' => [$license->{'notes'}],
1448                         'licenseKey' => [$license->{'licenseKey'}],
1449                         'hostId' => [$license->{'hostId'}],
1450                         'licensePoolId' => [$tmp_licensePool],
1451                         };
1452                 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1453                         $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1454                         map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1455                         map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1456                 }
1457                 push( @{$res_hash->{hit}}, $license_hash );
1458         }
1460         return $res_hash;
1463 ################################
1464 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1465 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1466 # @param licensePoolId The name of the pool (optional). 
1467 sub opsi_getSoftwareLicenseUsages {
1468         my $startTime = Time::HiRes::time;
1469         my ($msg, $msg_hash, $session_id) = @_;
1470         my $header = @{$msg_hash->{'header'}}[0];
1471         my $source = @{$msg_hash->{'source'}}[0];
1472         my $target = @{$msg_hash->{'target'}}[0];
1473         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1474         my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1475         my $out_hash;
1477         my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1478         if ($err){
1479                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1480         }
1482         # Parse Opsi result
1483         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1485         # Create function result message
1486         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1487         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1488         $out_hash->{result} = [$res_hash];
1490         my $endTime = Time::HiRes::time;
1491         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1492         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1493         return ( &create_xml_string($out_hash) );
1496 ################################
1497 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1498 # @param productId Something like 'firefox', 'python' or anything else .
1499 sub opsi_getSoftwareLicenseUsagesForProductId {
1500         my $startTime = Time::HiRes::time;
1501         my ($msg, $msg_hash, $session_id) = @_;
1502         my $header = @{$msg_hash->{'header'}}[0];
1503         my $source = @{$msg_hash->{'source'}}[0];
1505         # Check input sanity
1506         my $productId;
1507         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1508                 $productId= @{$msg_hash->{'productId'}}[0];
1509         } else {
1510                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1511         }
1513         # Fetch licensePoolId for productId
1514         my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1515         if ($err){
1516                 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1517         }
1519         my $licensePoolId;
1521         # Fetch softwareLiceceUsages for licensePoolId
1522         ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1523         if ($err){
1524                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1525         }
1527         # Parse Opsi result
1528         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1530         # Create function result message
1531         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1532         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1533         $out_hash->{result} = [$res_hash];
1535         my $endTime = Time::HiRes::time;
1536         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1537         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1538         return ( &create_xml_string($out_hash) );
1541 ################################
1542 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1543 # @param softwareLicenseId Identificator of a license.
1544 sub opsi_getSoftwareLicense_hash {
1545         my $startTime = Time::HiRes::time;
1546         my ($msg, $msg_hash, $session_id) = @_;
1547         my $header = @{$msg_hash->{'header'}}[0];
1548         my $source = @{$msg_hash->{'source'}}[0];
1549         my $target = @{$msg_hash->{'target'}}[0];
1550         my $softwareLicenseId;
1551         my $out_hash;
1553         # Check input sanity
1554         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1555                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1556         } else {
1557                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1558         }
1560         my $callobj = {
1561                 method  => 'getSoftwareLicense_hash',
1562                 params  => [ $softwareLicenseId ],
1563                 id  => 1,
1564         };
1565         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1567         # Check Opsi error
1568         my ($res_error, $res_error_str) = &check_opsi_res($res);
1569         if ($res_error){
1570                 # Create error message
1571                 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1572                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1573                 return ( &create_xml_string($out_hash) );
1574         }
1575         
1576         # Create function result message
1577         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1578         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1579         &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1580         &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1581         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1582         &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1583         foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1584                 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1585                 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1586         }
1588         my $endTime = Time::HiRes::time;
1589         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1590         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1591         return ( &create_xml_string($out_hash) );
1594 ################################
1595 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
1596 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted. 
1597 # @param licensePoolId The name of the pool. 
1598 sub opsi_deleteLicensePool {
1599         my $startTime = Time::HiRes::time;
1600         my ($msg, $msg_hash, $session_id) = @_;
1601     my $header = @{$msg_hash->{'header'}}[0];
1602     my $source = @{$msg_hash->{'source'}}[0];
1603     my $target = @{$msg_hash->{'target'}}[0];
1604     my $licensePoolId;
1605         my $out_hash;
1607         # Check input sanity
1608         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1609                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1610         } else {
1611                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1612         }
1614         # Fetch softwareLicenseIds used in license pool
1615         # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1616         # but not the license contracts of the software licenses. In our case each software license has exactly one license contract. 
1617         my $callobj = {
1618                 method  => 'getSoftwareLicenses_listOfHashes',
1619                 params  => [ ],
1620                 id  => 1,
1621         };
1622         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1624         # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1625         my @lCI_toBeDeleted;
1626         foreach my $softwareLicenseHash ( @{$res->result} ) {
1627                 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) { 
1628                         next; 
1629                 }  
1630                 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1631         }
1633         # Delete license pool at Opsi server
1634     $callobj = {
1635         method  => 'deleteLicensePool',
1636         params  => [ $licensePoolId, 'deleteLicenses=True'  ],
1637         id  => 1,
1638     };
1639     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1640         my ($res_error, $res_error_str) = &check_opsi_res($res);
1641         if ($res_error){
1642                 # Create error message
1643                 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1644                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1645                 return ( &create_xml_string($out_hash) );
1646         } 
1648         # Delete each license contract connected with the license pool
1649         foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1650                 my $callobj = {
1651                         method  => 'deleteLicenseContract',
1652                         params  => [ $licenseContractId ],
1653                         id  => 1,
1654                 };
1655                 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1656                 my ($res_error, $res_error_str) = &check_opsi_res($res);
1657                 if ($res_error){
1658                         # Create error message
1659                         &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1660                         $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1661                         return ( &create_xml_string($out_hash) );
1662                 }
1663         }
1665         # Create function result message
1666         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1667         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1669         my $endTime = Time::HiRes::time;
1670         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1671         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1672         return ( &create_xml_string($out_hash) );
1675 ################################
1676 # @brief Create a license contract, create a software license and add the software license to the license pool
1677 # @param licensePoolId The name of the pool the license should be assigned.
1678 # @param licenseKey The license key.
1679 # @param partner Name of the license partner (optional).
1680 # @param conclusionDate Date of conclusion of license contract (optional)
1681 # @param notificationDate Date of notification that license is running out soon (optional).
1682 # @param notes This is the place for some notes (optional)
1683 # @param softwareLicenseId Identificator of a license (optional).
1684 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1685 # @param maxInstallations The number of clients use this license (optional). 
1686 # @param boundToHost The name of the client the license is bound to (optional).
1687 # @param expirationDate The date when the license is running down (optional). 
1688 sub opsi_createLicense {
1689         my $startTime = Time::HiRes::time;
1690         my ($msg, $msg_hash, $session_id) = @_;
1691     my $header = @{$msg_hash->{'header'}}[0];
1692     my $source = @{$msg_hash->{'source'}}[0];
1693     my $target = @{$msg_hash->{'target'}}[0];
1694         my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1695         my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1696         my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1697         my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1698         my $licenseContractId = undef;
1699         my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1700         my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1701         my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1702         my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1703         my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1704         my $licensePoolId;
1705         my $licenseKey;
1706         my $out_hash;
1708         # Check input sanity
1709         if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1710                 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1711         } else {
1712                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1713         }
1714         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1715                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1716         } else {
1717                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1718         }
1719         if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1720                 return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
1721         }
1722         
1723         # Automatically define licenseContractId if ID is not given
1724         if (defined $softwareLicenseId) { 
1725                 $licenseContractId = "c_".$softwareLicenseId;
1726         }
1728         # Create license contract at Opsi server
1729     my $callobj = {
1730         method  => 'createLicenseContract',
1731         params  => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1732         id  => 1,
1733     };
1734     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1736         # Check Opsi error
1737         my ($res_error, $res_error_str) = &check_opsi_res($res);
1738         if ($res_error){
1739                 # Create error message
1740                 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1741                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1742                 return ( &create_xml_string($out_hash) );
1743         }
1744         
1745         $licenseContractId = $res->result;
1747         # Create software license at Opsi server
1748     $callobj = {
1749         method  => 'createSoftwareLicense',
1750         params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1751         id  => 1,
1752     };
1753     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1755         # Check Opsi error
1756         ($res_error, $res_error_str) = &check_opsi_res($res);
1757         if ($res_error){
1758                 # Create error message
1759                 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1760                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1761                 return ( &create_xml_string($out_hash) );
1762         }
1764         $softwareLicenseId = $res->result;
1766         # Add software license to license pool
1767         $callobj = {
1768         method  => 'addSoftwareLicenseToLicensePool',
1769         params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1770         id  => 1,
1771     };
1772     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1774         # Check Opsi error
1775         ($res_error, $res_error_str) = &check_opsi_res($res);
1776         if ($res_error){
1777                 # Create error message
1778                 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1779                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1780                 return ( &create_xml_string($out_hash) );
1781         }
1783         # Create function result message
1784         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1785         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1786         
1787         my $endTime = Time::HiRes::time;
1788         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1789         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1790         return ( &create_xml_string($out_hash) );
1793 ################################
1794 # @brief Assign a software license to a host
1795 # @param hostid Something like client_1.intranet.mydomain.de
1796 # @param licensePoolId The name of the pool.
1797 sub opsi_assignSoftwareLicenseToHost {
1798         my $startTime = Time::HiRes::time;
1799         my ($msg, $msg_hash, $session_id) = @_;
1800     my $header = @{$msg_hash->{'header'}}[0];
1801     my $source = @{$msg_hash->{'source'}}[0];
1802     my $target = @{$msg_hash->{'target'}}[0];
1803         my $hostId;
1804         my $licensePoolId;
1806         # Check input sanity
1807         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1808                 $hostId = @{$msg_hash->{'hostId'}}[0];
1809         } else {
1810                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1811         }
1812         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1813                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1814         } else {
1815                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1816         }
1818         # Assign a software license to a host
1819         my $callobj = {
1820         method  => 'getAndAssignSoftwareLicenseKey',
1821         params  => [ $hostId, $licensePoolId ],
1822         id  => 1,
1823     };
1824     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1826         # Check Opsi error
1827         my ($res_error, $res_error_str) = &check_opsi_res($res);
1828         if ($res_error){
1829                 # Create error message
1830                 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1831                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1832                 return ( &create_xml_string($out_hash) );
1833         }
1835         # Create function result message
1836         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1837         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1838         
1839         my $endTime = Time::HiRes::time;
1840         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1841         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1842         return ( &create_xml_string($out_hash) );
1845 ################################
1846 # @brief Unassign a software license from a host.
1847 # @param hostid Something like client_1.intranet.mydomain.de
1848 # @param licensePoolId The name of the pool.
1849 sub opsi_unassignSoftwareLicenseFromHost {
1850         my $startTime = Time::HiRes::time;
1851         my ($msg, $msg_hash, $session_id) = @_;
1852     my $header = @{$msg_hash->{'header'}}[0];
1853     my $source = @{$msg_hash->{'source'}}[0];
1854     my $target = @{$msg_hash->{'target'}}[0];
1855         my $hostId;
1856         my $licensePoolId;
1858         # Check input sanity
1859         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1860                 $hostId = @{$msg_hash->{'hostId'}}[0];
1861         } else {
1862                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1863         }
1864         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1865                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1866         } else {
1867                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1868         }
1870         # Unassign a software license from a host
1871         my $callobj = {
1872         method  => 'deleteSoftwareLicenseUsage',
1873         params  => [ $hostId, '', $licensePoolId ],
1874         id  => 1,
1875     };
1876     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1878         # Check Opsi error
1879         my ($res_error, $res_error_str) = &check_opsi_res($res);
1880         if ($res_error){
1881                 # Create error message
1882                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1883                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1884                 return ( &create_xml_string($out_hash) );
1885         }
1887         # Create function result message
1888         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1889         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1890         
1891         my $endTime = Time::HiRes::time;
1892         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1893         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1894         return ( &create_xml_string($out_hash) );
1897 ################################
1898 # @brief Unassign all software licenses from a host
1899 # @param hostid Something like client_1.intranet.mydomain.de
1900 sub opsi_unassignAllSoftwareLicensesFromHost {
1901         my $startTime = Time::HiRes::time;
1902         my ($msg, $msg_hash, $session_id) = @_;
1903     my $header = @{$msg_hash->{'header'}}[0];
1904     my $source = @{$msg_hash->{'source'}}[0];
1905     my $target = @{$msg_hash->{'target'}}[0];
1906         my $hostId;
1908         # Check input sanity
1909         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1910                 $hostId = @{$msg_hash->{'hostId'}}[0];
1911         } else {
1912                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1913         }
1915         # Unassign all software licenses from a host
1916         my $callobj = {
1917         method  => 'deleteAllSoftwareLicenseUsages',
1918         params  => [ $hostId ],
1919         id  => 1,
1920     };
1921     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1923         # Check Opsi error
1924         my ($res_error, $res_error_str) = &check_opsi_res($res);
1925         if ($res_error){
1926                 # Create error message
1927                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1928                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1929                 return ( &create_xml_string($out_hash) );
1930         }
1932         # Create function result message
1933         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1934         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1935         
1936         my $endTime = Time::HiRes::time;
1937         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1938         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1939         return ( &create_xml_string($out_hash) );
1943 ################################
1944 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1945 # and the number of max and remaining installations for a given OPSI product.
1946 # @param productId Identificator of an OPSI product.
1947 sub opsi_getLicenseInformationForProduct {
1948         my $startTime = Time::HiRes::time;
1949     my ($msg, $msg_hash, $session_id) = @_;
1950     my $header = @{$msg_hash->{'header'}}[0];
1951     my $source = @{$msg_hash->{'source'}}[0];
1952         my $productId;
1953         my $out_hash;
1955         # Check input sanity
1956         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1957                 $productId = @{$msg_hash->{'productId'}}[0];
1958         } else {
1959                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1960         }
1962         # Fetch infos from Opsi server
1963     my $callobj = {
1964         method  => 'getLicensePoolId',
1965         params  => [ $productId ],
1966         id  => 1,
1967     };
1968     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1970         # Check Opsi error
1971         my ($res_error, $res_error_str) = &check_opsi_res($res);
1972         if ($res_error){
1973                 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
1974         } 
1975         
1976         my $licensePoolId = $res->result;
1978         # Fetch statistic information for given pool ID
1979         $callobj = {
1980                 method  => 'getLicenseStatistics_hash',
1981                 params  => [ ],
1982                 id  => 1,
1983         };
1984         $res = $main::opsi_client->call($main::opsi_url, $callobj);
1986         # Check Opsi error
1987         ($res_error, $res_error_str) = &check_opsi_res($res);
1988         if ($res_error){
1989                 # Create error message
1990                 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
1991                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1992                 return ( &create_xml_string($out_hash) );
1993         }
1995         # Create function result message
1996         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1997         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1998         &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1999         &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
2000         &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
2001         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
2002         &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
2003         map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
2005         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2006         return ( &create_xml_string($out_hash) );
2010 ################################
2011 # @brief Returns licensePoolId, description, a list of productIds, al list of windowsSoftwareIds and a list of licenses for a given licensePoolId. 
2012 # Each license contains softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseKeys, hostIds, expirationDate, boundToHost and licenseContractId.
2013 # The licenseContract contains conclusionDate, expirationDate, notes, notificationDate and partner. 
2014 # @param licensePoolId The name of the pool.
2015 sub opsi_getPool {
2016         my $startTime = Time::HiRes::time;
2017     my ($msg, $msg_hash, $session_id) = @_;
2018     my $header = @{$msg_hash->{'header'}}[0];
2019     my $source = @{$msg_hash->{'source'}}[0];
2021         # Check input sanity
2022         my $licensePoolId;
2023         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2024                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2025         } else {
2026                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2027         }
2029         # Create hash for the answer
2030         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2031         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2033         # Call Opsi
2034         my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2035         if ($err){
2036                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2037         }
2038         # Add data to outgoing hash
2039         &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2040         &add_content2xml_hash($out_hash, "description", $res->{'description'});
2041         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2042         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2045         # Call Opsi two times
2046         my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2047         if ($usages_err){
2048                 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
2049         }
2050         my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2051         if ($licenses_err){
2052                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2053         }
2055         # Add data to outgoing hash
2056         # Parse through all software licenses and select those associated to the pool
2057         my $res_hash = { 'hit'=> [] };
2058         foreach my $license ( @$licenses_res) {
2059                 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2060                 my $found = 0;
2061                 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2062                 foreach my $lPI ( @licensePoolIds_list) {
2063                         if ($lPI eq $licensePoolId) { $found++ }
2064                 }
2065                 if (not $found ) { next; };
2066                 # Found matching licensePoolId
2067                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2068                         'licenseKeys' => {},
2069                         'expirationDate' => [$license->{'expirationDate'}],
2070                         'boundToHost' => [$license->{'boundToHost'}],
2071                         'maxInstallations' => [$license->{'maxInstallations'}],
2072                         'licenseType' => [$license->{'licenseType'}],
2073                         'licenseContractId' => [$license->{'licenseContractId'}],
2074                         'licensePoolIds' => [],
2075                         'hostIds' => [],
2076                         };
2077                 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2078                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2079                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2080                 }
2081                 foreach my $usage (@$usages_res) {
2082                         # Search for hostIds with matching softwareLicenseId
2083                         if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2084                                 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2085                         }
2086                 }
2088                 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
2089                 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
2090                 if ($lContract_err){
2091                         return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
2092                 }
2093                 $license_hash->{$license->{'licenseContractId'}} = [];
2094                 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
2095                         'notificationDate' => [$lContract_res->{notificationDate}],
2096                         'notes' => [$lContract_res->{notes}],
2097                         'exirationDate' => [$lContract_res->{expirationDate}],
2098                         'partner' => [$lContract_res->{partner}],
2099                 };
2100                 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2102                 push( @{$res_hash->{hit}}, $license_hash );
2103         }
2104         $out_hash->{licenses} = [$res_hash];
2106         my $endTime = Time::HiRes::time;
2107         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2108         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2109     return ( &create_xml_string($out_hash) );
2113 ################################
2114 # @brief Removes at first the software license from license pool and than deletes the software license. 
2115 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2116 # @param softwareLicenseId Identificator of a license.
2117 # @param licensePoolId The name of the pool.
2118 sub opsi_removeLicense {
2119         my $startTime = Time::HiRes::time;
2120     my ($msg, $msg_hash, $session_id) = @_;
2121     my $header = @{$msg_hash->{'header'}}[0];
2122     my $source = @{$msg_hash->{'source'}}[0];
2124         # Check input sanity
2125         my $softwareLicenseId;
2126         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2127                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2128         } else {
2129                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2130         }
2131         my $licensePoolId;
2132         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2133                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2134         } else {
2135                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2136         }
2137         
2138         # Call Opsi
2139         my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2140         if ($err){
2141                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2142         }
2144         # Call Opsi
2145         ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2146         if ($err){
2147                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2148         }
2150         # Create hash for the answer
2151         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2152         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2153         my $endTime = Time::HiRes::time;
2154         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2155         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2156         return ( &create_xml_string($out_hash) );
2160 ################################
2161 # @brief Return softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseContractId, expirationDate, boundToHost and a list of productIds.
2162 # @param hostId Something like client_1.intranet.mydomain.de
2163 sub opsi_getReservedLicenses {
2164         my $startTime = Time::HiRes::time;
2165         my ($msg, $msg_hash, $session_id) = @_;
2166         my $header = @{$msg_hash->{'header'}}[0];
2167         my $source = @{$msg_hash->{'source'}}[0];
2169         # Check input sanity
2170         my $hostId;
2171         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2172                 $hostId = @{$msg_hash->{'hostId'}}[0];
2173         } else {
2174                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2175         }
2177         # Fetch informations from Opsi server
2178         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2179         if ($license_err){
2180                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2181         }
2183         # Parse result
2184         my $res_hash = { 'hit'=> [] };
2185         foreach my $license ( @$license_res) {
2186                 if ($license->{boundToHost} ne $hostId) { next; }
2188                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2189                         'maxInstallations' => [$license->{'maxInstallations'}],
2190                         'boundToHost' => [$license->{'boundToHost'}],
2191                         'expirationDate' => [$license->{'expirationDate'}],
2192                         'licenseContractId' => [$license->{'licenseContractId'}],
2193                         'licenseType' => [$license->{'licenseType'}],
2194                         'licensePoolIds' => [],
2195                         };
2196                 
2197                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2198                         # Fetch information for license pools containing a software license which is bound to given host
2199                         my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2200                         if ($pool_err){
2201                                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2202                         }
2204                         # Add licensePool information to result hash
2205                         push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2206                         $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2207                         map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2208                         map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2209                 }
2210                 push( @{$res_hash->{hit}}, $license_hash );
2211         }
2212         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2213         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2214         $out_hash->{licenses} = [$res_hash];
2216         my $endTime = Time::HiRes::time;
2217         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2218         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2219     return ( &create_xml_string($out_hash) );
2222 ################################
2223 # @brief Bound the given softwareLicenseId to the given host.
2224 # @param hostId Opsi hostId
2225 # @param softwareLicenseId Identificator of a license (optional).
2226 sub opsi_boundHostToLicense {
2227         my $startTime = Time::HiRes::time;
2228         my ($msg, $msg_hash, $session_id) = @_;
2229         my $header = @{$msg_hash->{'header'}}[0];
2230         my $source = @{$msg_hash->{'source'}}[0];
2232         # Check input sanity
2233         my $hostId;
2234         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2235                 $hostId = @{$msg_hash->{'hostId'}}[0];
2236         } else {
2237                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2238         }
2239         my $softwareLicenseId;
2240         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2241                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2242         } else {
2243                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2244         }
2246         # Fetch informations from Opsi server
2247         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2248         if ($license_err){
2249                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2250         }
2252         # Memorize parameter for given softwareLicenseId
2253         my $licenseContractId;
2254         my $licenseType;
2255         my $maxInstallations;
2256         my $boundToHost;
2257         my $expirationDate = "";
2258         my $found;
2259         foreach my $license (@$license_res) {
2260                 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2261                 $licenseContractId = $license->{licenseContractId};
2262                 $licenseType = $license->{licenseType};
2263                 $maxInstallations = $license->{maxInstallations};
2264                 $expirationDate = $license->{expirationDate};
2265                 $found++;
2266         }
2268         if (not $found) {
2269                 return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
2270         }
2272         # Set boundToHost option for a given software license
2273         my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId, 
2274                         'licenseContractId' => $licenseContractId, 
2275                         'licenseType' => $licenseType, 
2276                         'maxInstallations' => $maxInstallations, 
2277                         'boundToHost' => $hostId, 
2278                         'expirationDate' => $expirationDate);
2279         if ($bound_err) {
2280                 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2281         }
2283         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2284         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2286         my $endTime = Time::HiRes::time;
2287         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2288         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2289     return ( &create_xml_string($out_hash) );
2292 ################################
2293 # @brief Release a software license formerly bound to a host.
2294 # @param softwareLicenseId Identificator of a license.
2295 sub opsi_unboundHostFromLicense {
2296         # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2297         # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2298         # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2299         my $startTime = Time::HiRes::time;
2300         my ($msg, $msg_hash, $session_id) = @_;
2301         my $header = @{$msg_hash->{'header'}}[0];
2302         my $source = @{$msg_hash->{'source'}}[0];
2304         # Check input sanity
2305         my $softwareLicenseId;
2306         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2307                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2308         } else {
2309                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2310         }
2311         
2312         # Memorize parameter witch are required for this procedure
2313         my $licenseContractId;
2314         my $licenseType;
2315         my $maxInstallations;
2316         my $expirationDate;
2317         my $partner;
2318         my $conclusionDate;
2319         my $notificationDate;
2320         my $notes;
2321         my $licensePoolId;
2322         my $licenseKey;
2324         # Fetch license informations from Opsi server
2325         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2326         if ($license_err){
2327                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2328         }
2329         my $found = 0;
2330         foreach my $license (@$license_res) {
2331                 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2332                 $licenseContractId = $license->{licenseContractId};
2333                 $licenseType = $license->{licenseType};
2334                 $maxInstallations = $license->{maxInstallations};
2335                 $expirationDate = $license->{expirationDate};
2336                 $licensePoolId = @{$license->{licensePoolIds}}[0];
2337                 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2338                 $found++;
2339         }
2340         
2341         # Fetch contract informations from Opsi server
2342         my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2343         if ($contract_err){
2344                 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2345         }
2346         $partner = $contract_res->{partner};
2347         $conclusionDate = $contract_res->{conclusionDate};
2348         $notificationDate = $contract_res->{notificationDate};
2349         $expirationDate = $contract_res->{expirationDate};
2350         $notes = $contract_res->{notes};
2352         # Delete software license
2353         my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2354         if ($err) {
2355                 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2356         }
2358         # Recreate software license without boundToHost
2359         ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate, 
2360                         'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2361         if ($err) {
2362                 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2363         }
2364         ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType, 
2365                         'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate       );
2366         if ($err) {
2367                 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2368         }
2369         ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2370         if ($err) {
2371                 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2372         }
2374         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2375         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2377         my $endTime = Time::HiRes::time;
2378         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2379         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2380     return ( &create_xml_string($out_hash) );
2383 ################################
2384 # @brief Returns a list of licenses with softwaerLicenseId, maxInstallations, boundToHost, expirationDate, licenseContractId, licenseType, a list of licensePoolIds with associated licenseKeys
2385 sub opsi_getAllSoftwareLicenses {
2386         my $startTime = Time::HiRes::time;
2387         my ($msg, $msg_hash, $session_id) = @_;
2388         my $header = @{$msg_hash->{'header'}}[0];
2389         my $source = @{$msg_hash->{'source'}}[0];
2391         my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
2392         if ($err) {
2393                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
2394         }
2396         # Parse result
2397         my $res_hash = { 'hit'=> [] };
2398         foreach my $license ( @$res) {
2399                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2400                         'maxInstallations' => [$license->{'maxInstallations'}],
2401                         'boundToHost' => [$license->{'boundToHost'}],
2402                         'expirationDate' => [$license->{'expirationDate'}],
2403                         'licenseContractId' => [$license->{'licenseContractId'}],
2404                         'licenseType' => [$license->{'licenseType'}],
2405                         'licensePoolIds' => [],
2406                         'licenseKeys'=> {}
2407                         };
2408                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2409                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2410                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2411                 }
2412                 push( @{$res_hash->{hit}}, $license_hash );
2413         }
2414         
2415         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2416         $out_hash->{licenses} = [$res_hash];
2417         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2419         my $endTime = Time::HiRes::time;
2420         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2421         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2422     return ( &create_xml_string($out_hash) );
2425 sub opsi_test {
2426     my ($msg, $msg_hash, $session_id) = @_;
2427     my $header = @{$msg_hash->{'header'}}[0];
2428     my $source = @{$msg_hash->{'source'}}[0];
2429         my $pram1 = @{$msg_hash->{'productId'}}[0];
2431 print STDERR Dumper $pram1;
2433         # Fetch infos from Opsi server
2434     my $callobj = {
2435         method  => 'getLicensePoolId',
2436         params  => [ $pram1 ],
2437         id  => 1,
2438     };
2439     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2441         print STDERR Dumper $res;
2442         return ();
2446 # ----------------------------------------------------------------------------
2447 #  internal methods handling the comunication with Opsi
2448 # ----------------------------------------------------------------------------
2450 ################################
2451 # @brief Checks if there is a specified tag and if the the tag has a content.
2452 sub _check_xml_tag_is_ok {
2453         my ($msg_hash,$tag) = @_;
2454         if (not defined $msg_hash->{$tag}) {
2455                 $_ = "message contains no tag '$tag'";
2456                 return 0;
2457         }
2458         if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
2459                 $_ = "message tag '$tag' has no content";
2460                 return  0;
2461         }
2462         return 1;
2465 ################################
2466 # @brief Writes the log line and returns the error message for GOsa.
2467 sub _giveErrorFeedback {
2468         my ($msg_hash, $err_string, $session_id) = @_;
2469         &main::daemon_log("$session_id ERROR: $err_string", 1);
2470         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2471     if (exists $msg_hash->{forward_to_gosa}) {
2472         &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
2473     }
2474         return ( &create_xml_string($out_hash) );
2478 ################################
2479 # @brief Perform the call to the Opsi server and measure the time for the call
2480 sub _callOpsi {
2481         my %arg = ('method'=>undef, 'params'=>[], 'id'=>1, @_);
2483         my $callObject = {
2484                 method => $arg{method},
2485                 params => $arg{params},
2486                 id => $arg{id},
2487         };
2489         my $startTime = Time::HiRes::time;
2490         my $opsiResult = $main::opsi_client->call($main::opsi_url, $callObject);
2491         my $endTime = Time::HiRes::time;
2492         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2494         &main::daemon_log("0 DEBUG: time to process opsi call '$arg{method}' : $elapsedTime seconds", 1034); 
2496         return $opsiResult;
2499 sub _getLicensePool_hash {
2500         my %arg = ( 'licensePoolId' => undef, @_ );
2502         if (not defined $arg{licensePoolId} ) { 
2503                 return ("function requires licensePoolId as parameter", 1);
2504         }
2506         my $res = &_callOpsi( method  => 'getLicensePool_hash', params =>[$arg{licensePoolId}], id  => 1 );
2507         my ($res_error, $res_error_str) = &check_opsi_res($res);
2508         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2510         return ($res->result, 0);
2513 sub _getSoftwareLicenses_listOfHashes {
2514         
2515         my $res = &_callOpsi( method  => 'getSoftwareLicenses_listOfHashes' );
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 _getSoftwareLicenseUsages_listOfHashes {
2523         my %arg = ( 'hostId' => "", 'licensePoolId' => "", @_ );
2525         my $res = &_callOpsi( method=>'getSoftwareLicenseUsages_listOfHashes', params=>[ $arg{hostId}, $arg{licensePoolId} ] );
2526         my ($res_error, $res_error_str) = &check_opsi_res($res);
2527         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2529         return ($res->result, 0);
2532 sub _removeSoftwareLicenseFromLicensePool {
2533         my %arg = ( 'softwareLicenseId' => undef, 'licensePoolId' => undef, @_ );
2535         if (not defined $arg{softwareLicenseId} ) { 
2536                 return ("function requires softwareLicenseId as parameter", 1);
2537                 }
2538                 if (not defined $arg{licensePoolId} ) { 
2539                 return ("function requires licensePoolId as parameter", 1);
2540         }
2542         my $res = &_callOpsi( method=>'removeSoftwareLicenseFromLicensePool', params=>[ $arg{softwareLicenseId}, $arg{licensePoolId} ] );
2543         my ($res_error, $res_error_str) = &check_opsi_res($res);
2544         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2546         return ($res->result, 0);
2549 sub _deleteSoftwareLicense {
2550         my %arg = ( 'softwareLicenseId' => undef, 'removeFromPools' => "false", @_ );
2552         if (not defined $arg{softwareLicenseId} ) { 
2553                 return ("function requires softwareLicenseId as parameter", 1);
2554         }
2555         my $removeFromPools = "";
2556         if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) { 
2557                 $removeFromPools = "removeFromPools";
2558         }
2560         my $res = &_callOpsi( method=>'deleteSoftwareLicense', params=>[ $arg{softwareLicenseId}, $removeFromPools ] );
2561         my ($res_error, $res_error_str) = &check_opsi_res($res);
2562         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2564         return ($res->result, 0);
2567 sub _getLicensePoolId {
2568         my %arg = ( 'productId' => undef, @_ );
2569         
2570         if (not defined $arg{productId} ) {
2571                 return ("function requires productId as parameter", 1);
2572         }
2574     my $res = &_callOpsi( method  => 'getLicensePoolId', params  => [ $arg{productId} ] );
2575         my ($res_error, $res_error_str) = &check_opsi_res($res);
2576         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2578         return ($res->result, 0);
2581 sub _getLicenseContract_hash {
2582         my %arg = ( 'licenseContractId' => undef, @_ );
2583         
2584         if (not defined $arg{licenseContractId} ) {
2585                 return ("function requires licenseContractId as parameter", 1);
2586         }
2588     my $res = &_callOpsi( method  => 'getLicenseContract_hash', params  => [ $arg{licenseContractId} ] );
2589         my ($res_error, $res_error_str) = &check_opsi_res($res);
2590         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2592         return ($res->result, 0);
2595 sub _createLicenseContract {
2596         my %arg = (
2597                         'licenseContractId' => undef,
2598                         'partner' => undef,
2599                         'conclusionDate' => undef,
2600                         'notificationDate' => undef,
2601                         'expirationDate' => undef,
2602                         'notes' => undef,
2603                         @_ );
2605         my $res = &_callOpsi( method  => 'createLicenseContract', 
2606                         params  => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2607                         );
2608         my ($res_error, $res_error_str) = &check_opsi_res($res);
2609         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2611         return ($res->result, 0);
2614 sub _createSoftwareLicense {
2615         my %arg = (
2616                         'softwareLicenseId' => undef,
2617                         'licenseContractId' => undef,
2618                         'licenseType' => undef,
2619                         'maxInstallations' => undef,
2620                         'boundToHost' => undef,
2621                         'expirationDate' => undef,
2622                         @_ );
2624     my $res = &_callOpsi( method  => 'createSoftwareLicense',
2625         params  => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2626                 );
2627         my ($res_error, $res_error_str) = &check_opsi_res($res);
2628         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2630         return ($res->result, 0);
2633 sub _addSoftwareLicenseToLicensePool {
2634         my %arg = (
2635             'softwareLicenseId' => undef,
2636             'licensePoolId' => undef,
2637             'licenseKey' => undef,
2638             @_ );
2640         if (not defined $arg{softwareLicenseId} ) {
2641                 return ("function requires softwareLicenseId as parameter", 1);
2642         }
2643         if (not defined $arg{licensePoolId} ) {
2644                 return ("function requires licensePoolId as parameter", 1);
2645         }
2647         my $res = &_callOpsi( method  => 'addSoftwareLicenseToLicensePool', params  => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ] );
2648         my ($res_error, $res_error_str) = &check_opsi_res($res);
2649         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2651         return ($res->result, 0);
2654 1;