Code

* new function opsi_get_full_product_host_information
[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_get_full_product_host_information",
18     "opsi_set_product_properties",
19     "opsi_list_clients",
20     "opsi_del_client",
21     "opsi_add_client",
22     "opsi_modify_client",
23     "opsi_add_product_to_client",
24     "opsi_del_product_from_client",
25         "opsi_createLicensePool",
26         "opsi_deleteLicensePool",
27         "opsi_createLicense",
28         "opsi_assignSoftwareLicenseToHost",
29         "opsi_unassignSoftwareLicenseFromHost",
30         "opsi_unassignAllSoftwareLicensesFromHost",
31         "opsi_getSoftwareLicense_hash",
32         "opsi_getLicensePool_hash",
33         "opsi_getSoftwareLicenseUsages",
34         "opsi_getSoftwareLicenseUsagesForProductId",
35         "opsi_getLicensePools_listOfHashes",
36         "opsi_getLicenseInformationForProduct",
37         "opsi_getPool",
38         "opsi_getAllSoftwareLicenses",
39         "opsi_removeLicense",
40         "opsi_getReservedLicenses",
41         "opsi_boundHostToLicense",
42         "opsi_unboundHostFromLicense",
43         "opsi_get_full_product_host_information",
44         "opsi_test",
45    );
46 @EXPORT = @events;
48 use strict;
49 use warnings;
50 use GOSA::GosaSupportDaemon;
51 use Data::Dumper;
52 use XML::Quote qw(:all);
54 BEGIN {}
56 END {}
58 # ----------------------------------------------------------------------------
59 #                          D E C L A R A T I O N S
60 # ----------------------------------------------------------------------------
62 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
63 my ($opsi_enabled, $opsi_server, $opsi_admin, $opsi_password, $opsi_url, $opsi_client);
64 my %cfg_defaults = (
65                 "Opsi" => {
66                 "enabled"  => [\$opsi_enabled, "false"],
67                 "server"   => [\$opsi_server, "localhost"],
68                 "admin"    => [\$opsi_admin, "opsi-admin"],
69                 "password" => [\$opsi_password, "secret"],
70                 },
71 );
72 &read_configfile($main::cfg_file, %cfg_defaults);
73 if ($opsi_enabled eq "true") {
74         use JSON::RPC::Client;
75         use XML::Quote qw(:all);
76         use Time::HiRes qw( time );
77         $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
78         $opsi_client = new JSON::RPC::Client;
80         # Check version dependencies
81         eval { &myXmlHashToString(); };
82         if ($@ ) {
83                 die "\nThe version of the Opsi plugin you want to use requires a newer version of GosaSupportDaemon. Please update your GOsa-SI or deactivate the Opsi plugin.\n";
84         }
85 }
87 # ----------------------------------------------------------------------------
88 #   external methods handling the comunication with GOsa/GOsa-si
89 # ----------------------------------------------------------------------------
91 ################################
92 # @brief A function returning a list of functions which are exported by importing the module.
93 # @return List of all provided functions
94 sub get_events {
95     return \@events;
96 }
98 ################################
99 # @brief Adds an Opsi product to an Opsi client.
100 # @param msg - STRING - xml message with tags hostId and productId
101 # @param msg_hash - HASHREF - message information parsed into a hash
102 # @param session_id - INTEGER - POE session id of the processing of this message
103 # @return out_msg - STRING - feedback to GOsa in success and error case
104 sub opsi_add_product_to_client {
105         my $startTime = Time::HiRes::time;
106     my ($msg, $msg_hash, $session_id) = @_;
107     my $header = @{$msg_hash->{'header'}}[0];
108     my $source = @{$msg_hash->{'source'}}[0];
109     my $target = @{$msg_hash->{'target'}}[0];
110     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
112     # Build return message
113     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
114     if (defined $forward_to_gosa) {
115         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
116     }
118     # Sanity check of needed parameter
119     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
120                 return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
121     }
122     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
123                 return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
124     }
126         # Get hostId
127         my $hostId = @{$msg_hash->{'hostId'}}[0];
128         &add_content2xml_hash($out_hash, "hostId", $hostId);
130         # Get productID
131         my $productId = @{$msg_hash->{'productId'}}[0];
132         &add_content2xml_hash($out_hash, "productId", $productId);
134         # Do an action request for all these -> "setup".
135         my $callobj = {
136                 method  => 'setProductActionRequest',
137                 params  => [ $productId, $hostId, "setup" ],
138                 id  => 1, }; 
139         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
141         if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
143         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
144     return ( &create_xml_string($out_hash) );
147 ################################
148 # @brief Deletes an Opsi-product from an Opsi-client. 
149 # @param msg - STRING - xml message with tags hostId and productId
150 # @param msg_hash - HASHREF - message information parsed into a hash
151 # @param session_id - INTEGER - POE session id of the processing of this message
152 # @return out_msg - STRING - feedback to GOsa in success and error case
153 sub opsi_del_product_from_client {
154         my $startTime = Time::HiRes::time;
155     my ($msg, $msg_hash, $session_id) = @_;
156     my $header = @{$msg_hash->{'header'}}[0];
157     my $source = @{$msg_hash->{'source'}}[0];
158     my $target = @{$msg_hash->{'target'}}[0];
159     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
160     my ($hostId, $productId);
161     my $error = 0;
162     my ($sres, $sres_err, $sres_err_string);
164     # Build return message
165     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
166     if (defined $forward_to_gosa) {
167         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
168     }
170     # Sanity check of needed parameter
171     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
172         $error++;
173         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
174         &add_content2xml_hash($out_hash, "error", "hostId");
175         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
177     }
178     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
179         $error++;
180         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
181         &add_content2xml_hash($out_hash, "error", "productId");
182         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
183     }
185     # All parameter available
186     if (not $error) {
187         # Get hostId
188         $hostId = @{$msg_hash->{'hostId'}}[0];
189         &add_content2xml_hash($out_hash, "hostId", $hostId);
191         # Get productID
192         $productId = @{$msg_hash->{'productId'}}[0];
193         &add_content2xml_hash($out_hash, "productId", $productId);
195         # Check to get product action list 
196         my $callobj = {
197             method  => 'getPossibleProductActions_list',
198             params  => [ $productId ],
199             id  => 1, };
200         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
201         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
202         if ($sres_err){
203             &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
204             &add_content2xml_hash($out_hash, "error", $sres_err_string);
205             $error++;
206         }
207     }
209     # Check action uninstall of product
210     if (not $error) {
211         my $uninst_possible= 0;
212         foreach my $r (@{$sres->result}) {
213             if ($r eq 'uninstall') {
214                 $uninst_possible= 1;
215             }
216         }
217         if (!$uninst_possible){
218             &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
219             &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
220             $error++;
221         }
222     }
224     # Set product state to "none"
225     # Do an action request for all these -> "setup".
226     if (not $error) {
227         my $callobj = {
228             method  => 'setProductActionRequest',
229             params  => [ $productId, $hostId, "none" ],
230             id  => 1, 
231         }; 
232         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
233         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
234         if ($sres_err){
235             &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
236             &add_content2xml_hash($out_hash, "error", $sres_err_string);
237         }
238     }
240     # Return message
241         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
242     return ( &create_xml_string($out_hash) );
245 ################################
246 # @brief Adds an Opsi client to Opsi.
247 # @param msg - STRING - xml message with tags hostId and macaddress
248 # @param msg_hash - HASHREF - message information parsed into a hash
249 # @param session_id - INTEGER - POE session id of the processing of this message
250 # @return out_msg - STRING - feedback to GOsa in success and error case
251 sub opsi_add_client {
252         my $startTime = Time::HiRes::time;
253     my ($msg, $msg_hash, $session_id) = @_;
254     my $header = @{$msg_hash->{'header'}}[0];
255     my $source = @{$msg_hash->{'source'}}[0];
256     my $target = @{$msg_hash->{'target'}}[0];
257     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
258     my ($hostId, $mac);
259     my $error = 0;
260     my ($sres, $sres_err, $sres_err_string);
262     # Build return message with twisted target and source
263     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
264     if (defined $forward_to_gosa) {
265         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
266     }
268     # Sanity check of needed parameter
269     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
270         $error++;
271         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
272         &add_content2xml_hash($out_hash, "error", "hostId");
273         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
274     }
275     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
276         $error++;
277         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
278         &add_content2xml_hash($out_hash, "error", "macaddress");
279         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
280     }
282     if (not $error) {
283         # Get hostId
284         $hostId = @{$msg_hash->{'hostId'}}[0];
285         &add_content2xml_hash($out_hash, "hostId", $hostId);
287         # Get macaddress
288         $mac = @{$msg_hash->{'macaddress'}}[0];
289         &add_content2xml_hash($out_hash, "macaddress", $mac);
291         my $name= $hostId;
292         $name=~ s/^([^.]+).*$/$1/;
293         my $domain= $hostId;
294         $domain=~ s/^[^.]+\.(.*)$/$1/;
295         my ($description, $notes, $ip);
297         if (defined @{$msg_hash->{'description'}}[0]){
298             $description = @{$msg_hash->{'description'}}[0];
299         }
300         if (defined @{$msg_hash->{'notes'}}[0]){
301             $notes = @{$msg_hash->{'notes'}}[0];
302         }
303         if (defined @{$msg_hash->{'ip'}}[0]){
304             $ip = @{$msg_hash->{'ip'}}[0];
305         }
307         my $callobj;
308         $callobj = {
309             method  => 'createClient',
310             params  => [ $name, $domain, $description, $notes, $ip, $mac ],
311             id  => 1,
312         };
314         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
315         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
316         if ($sres_err){
317             &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
318             &add_content2xml_hash($out_hash, "error", $sres_err_string);
319         } else {
320             &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5); 
321         }
322     }
324     # Return message
325         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
326     return ( &create_xml_string($out_hash) );
329 ################################
330 # @brief Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
331 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
332 # @param msg_hash - HASHREF - message information parsed into a hash
333 # @param session_id - INTEGER - POE session id of the processing of this message    
334 # @return out_msg - STRING - feedback to GOsa in success and error case
335 sub opsi_modify_client {
336         my $startTime = Time::HiRes::time;
337     my ($msg, $msg_hash, $session_id) = @_;
338     my $header = @{$msg_hash->{'header'}}[0];
339     my $source = @{$msg_hash->{'source'}}[0];
340     my $target = @{$msg_hash->{'target'}}[0];
341     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
342     my $hostId;
343     my $error = 0;
344     my ($sres, $sres_err, $sres_err_string);
346     # Build return message with twisted target and source
347     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
348     if (defined $forward_to_gosa) {
349         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
350     }
352     # Sanity check of needed parameter
353     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
354         $error++;
355         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
356         &add_content2xml_hash($out_hash, "error", "hostId");
357         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
358     }
360     if (not $error) {
361         # Get hostId
362         $hostId = @{$msg_hash->{'hostId'}}[0];
363         &add_content2xml_hash($out_hash, "hostId", $hostId);
364         my $name= $hostId;
365         $name=~ s/^([^.]+).*$/$1/;
366         my $domain= $hostId;
367         $domain=~ s/^[^.]+(.*)$/$1/;
369         # Modify description, notes or mac if defined
370         my ($description, $notes, $mac);
371         my $callobj;
372         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
373             $description = @{$msg_hash->{'description'}}[0];
374             $callobj = {
375                 method  => 'setHostDescription',
376                 params  => [ $hostId, $description ],
377                 id  => 1,
378             };
379             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
380             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
381             if ($sres_err){
382                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
383                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
384             }
385         }
386         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
387             $notes = @{$msg_hash->{'notes'}}[0];
388             $callobj = {
389                 method  => 'setHostNotes',
390                 params  => [ $hostId, $notes ],
391                 id  => 1,
392             };
393             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
394             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
395             if ($sres_err){
396                 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
397                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
398             }
399         }
400         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
401             $mac = @{$msg_hash->{'mac'}}[0];
402             $callobj = {
403                 method  => 'setMacAddress',
404                 params  => [ $hostId, $mac ],
405                 id  => 1,
406             };
407             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
408             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
409             if ($sres_err){
410                 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
411                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
412             }
413         }
414     }
416     # Return message
417         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
418     return ( &create_xml_string($out_hash) );
420  
421 ################################
422 # @brief Get netboot products for specific host.
423 # @param msg - STRING - xml message with tag hostId
424 # @param msg_hash - HASHREF - message information parsed into a hash
425 # @param session_id - INTEGER - POE session id of the processing of this message
426 # @return out_msg - STRING - feedback to GOsa in success and error case
427 sub opsi_get_netboot_products {
428     my $startTime = Time::HiRes::time;
429     my ($msg, $msg_hash, $session_id) = @_;
430     my $header = @{$msg_hash->{'header'}}[0];
431     my $source = @{$msg_hash->{'source'}}[0];
432     my $target = @{$msg_hash->{'target'}}[0];
433     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
434     my $hostId;
436     # Build return message with twisted target and source
437     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
438     if (defined $forward_to_gosa) {
439         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
440     }
441     &add_content2xml_hash($out_hash, "xxx", "");
443     # Get hostId if defined
444     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
445         $hostId = @{$msg_hash->{'hostId'}}[0];
446         &add_content2xml_hash($out_hash, "hostId", $hostId);
447     }
449     # Move to XML string
450     my $xml_msg= &create_xml_string($out_hash);
452     my $callobj;
453     # Check if we need to get host or global information
454     if (defined $hostId){
455       $callobj = {
456           method  => 'getProductHostInformation_list',
457           params  => [ $hostId, undef, 'netboot'],
458           id  => 1,
459       };
461       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
462       if (not &check_opsi_res($res)){
463           foreach my $product (@{$res->result}){
464                my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><state>".xml_quote($product->{'installationStatus'})."</state><action>".xml_quote($product->{'actionRequest'})."</action><\/item><xxx><\/xxx>";
465                $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
466           }
467       }
469     } else {
471       # For hosts, only return the products that are or get installed
472       $callobj = {
473           method  => 'getProductInformation_list',
474           params  => [ undef, 'netboot' ],
475           id  => 1,
476       };
478       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
479       if (not &check_opsi_res($res)){
480           foreach my $product (@{$res->result}) {
481                my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
482                $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
483           }
484       }
485     }
487     $xml_msg=~ s/<xxx><\/xxx>//;
489     # Retrun Message
490         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
491     return ( $xml_msg );
494 ################################   
495 # @brief Get product properties for a product and a specific host or gobally for a product.
496 # @param msg - STRING - xml message with tags productId and optional hostId
497 # @param msg_hash - HASHREF - message information parsed into a hash
498 # @param session_id - INTEGER - POE session id of the processing of this message
499 # @return out_msg - STRING - feedback to GOsa in success and error case
500 sub opsi_get_product_properties {
501         my $startTime = Time::HiRes::time;
502     my ($msg, $msg_hash, $session_id) = @_;
503     my $header = @{$msg_hash->{'header'}}[0];
504     my $source = @{$msg_hash->{'source'}}[0];
505     my $target = @{$msg_hash->{'target'}}[0];
506     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
507     my ($hostId, $productId);
508     my $xml_msg;
510     # Build return message with twisted target and source
511     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
512     if (defined $forward_to_gosa) {
513         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
514     }
516     # Sanity check of needed parameter
517     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
518         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
519         &add_content2xml_hash($out_hash, "error", "productId");
520         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
522         # Return message
523         return ( &create_xml_string($out_hash) );
524     }
526     # Get productid
527     $productId = @{$msg_hash->{'productId'}}[0];
528     &add_content2xml_hash($out_hash, "producId", "$productId");
530     # Get hostId if defined
531     if (defined @{$msg_hash->{'hostId'}}[0]){
532       $hostId = @{$msg_hash->{'hostId'}}[0];
533       &add_content2xml_hash($out_hash, "hostId", $hostId);
534     }
536     # Load actions
537     my $callobj = {
538       method  => 'getPossibleProductActions_list',
539       params  => [ $productId ],
540       id  => 1,
541     };
542     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
543     if (not &check_opsi_res($res)){
544       foreach my $action (@{$res->result}){
545         &add_content2xml_hash($out_hash, "action", $action);
546       }
547     }
549     # Add place holder
550     &add_content2xml_hash($out_hash, "xxx", "");
552     # Move to XML string
553     $xml_msg= &create_xml_string($out_hash);
555     # JSON Query
556     if (defined $hostId){
557       $callobj = {
558           method  => 'getProductProperties_hash',
559           params  => [ $productId, $hostId ],
560           id  => 1,
561       };
562     } else {
563       $callobj = {
564           method  => 'getProductProperties_hash',
565           params  => [ $productId ],
566           id  => 1,
567       };
568     }
569     $res = $main::opsi_client->call($main::opsi_url, $callobj);
571     # JSON Query 2
572     $callobj = {
573       method  => 'getProductPropertyDefinitions_listOfHashes',
574       params  => [ $productId ],
575       id  => 1,
576     };
578     # Assemble options
579     my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
580     my $values = {};
581     my $descriptions = {};
582     if (not &check_opsi_res($res2)){
583         my $r= $res2->result;
585           foreach my $entr (@$r){
586             # Unroll values
587             my $cnv;
588             if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
589               foreach my $v (@{$entr->{'values'}}){
590                 $cnv.= "<value>$v</value>";
591               }
592             } else {
593               $cnv= $entr->{'values'};
594             }
595             $values->{$entr->{'name'}}= $cnv;
596             $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
597           }
598     }
600     if (not &check_opsi_res($res)){
601         my $r= $res->result;
602         foreach my $key (keys %{$r}) {
603             my $item= "\n<item>";
604             my $value= $r->{$key};
605             my $dsc= "";
606             my $vals= "";
607             if (defined $descriptions->{$key}){
608               $dsc= $descriptions->{$key};
609             }
610             if (defined $values->{$key}){
611               $vals= $values->{$key};
612             }
613             $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
614             $item.= "</item>";
615             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
616         }
617     }
619     $xml_msg=~ s/<xxx><\/xxx>//;
621     # Return message
622         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
623     return ( $xml_msg );
626 ################################   
627 # @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.
628 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
629 # @param msg_hash - HASHREF - message information parsed into a hash
630 # @param session_id - INTEGER - POE session id of the processing of this message
631 # @return out_msg - STRING - feedback to GOsa in success and error case
632 sub opsi_set_product_properties {
633         my $startTime = Time::HiRes::time;
634     my ($msg, $msg_hash, $session_id) = @_;
635     my $header = @{$msg_hash->{'header'}}[0];
636     my $source = @{$msg_hash->{'source'}}[0];
637     my $target = @{$msg_hash->{'target'}}[0];
638     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
639     my ($productId, $hostId);
641     # Build return message with twisted target and source
642     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
643     if (defined $forward_to_gosa) {
644         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
645     }
647     # Sanity check of needed parameter
648     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
649         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
650         &add_content2xml_hash($out_hash, "error", "productId");
651         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
652         return ( &create_xml_string($out_hash) );
653     }
654     if (not exists $msg_hash->{'item'}) {
655         &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
656         &add_content2xml_hash($out_hash, "error", "item");
657         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
658         return ( &create_xml_string($out_hash) );
659     } else {
660         if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
661             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
662             &add_content2xml_hash($out_hash, "error", "name");
663             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
664             return ( &create_xml_string($out_hash) );
665         }
666         if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
667             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
668             &add_content2xml_hash($out_hash, "error", "value");
669             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
670             return ( &create_xml_string($out_hash) );
671         }
672     }
673     # if no hostId is given, set_product_properties will act on globally
674     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
675         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
676         &add_content2xml_hash($out_hash, "error", "hostId");
677         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
678         return ( &create_xml_string($out_hash) );
679     }
681         
682     # Get productId
683     $productId =  @{$msg_hash->{'productId'}}[0];
684     &add_content2xml_hash($out_hash, "productId", $productId);
686     # Get hostId if defined
687     if (exists $msg_hash->{'hostId'}){
688         $hostId = @{$msg_hash->{'hostId'}}[0];
689         &add_content2xml_hash($out_hash, "hostId", $hostId);
690     }
692     # Set product states if requested
693     if (defined @{$msg_hash->{'action'}}[0]){
694         &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
695     }
696     if (defined @{$msg_hash->{'state'}}[0]){
697         &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
698     }
700     # Find properties
701     foreach my $item (@{$msg_hash->{'item'}}){
702         # JSON Query
703         my $callobj;
705         if (defined $hostId){
706             $callobj = {
707                 method  => 'setProductProperty',
708                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
709                 id  => 1,
710             };
711         } else {
712             $callobj = {
713                 method  => 'setProductProperty',
714                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
715                 id  => 1,
716             };
717         }
719         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
720         my ($res_err, $res_err_string) = &check_opsi_res($res);
722         if ($res_err){
723             &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
724             &add_content2xml_hash($out_hash, "error", $res_err_string);
725         }
726     }
729     # Return message
730         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
731     return ( &create_xml_string($out_hash) );
734 ################################   
735 # @brief Reports client hardware inventory.
736 # @param msg - STRING - xml message with tag hostId
737 # @param msg_hash - HASHREF - message information parsed into a hash
738 # @param session_id - INTEGER - POE session id of the processing of this message
739 # @return out_msg - STRING - feedback to GOsa in success and error case
740 sub opsi_get_client_hardware {
741         my $startTime = Time::HiRes::time;
742     my ($msg, $msg_hash, $session_id) = @_;
743     my $header = @{$msg_hash->{'header'}}[0];
744     my $source = @{$msg_hash->{'source'}}[0];
745     my $target = @{$msg_hash->{'target'}}[0];
746     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
747     my $hostId;
748     my $error = 0;
749     my $xml_msg;
751     # Sanity check of needed parameter
752         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
753         $hostId = @{$msg_hash->{'hostId'}}[0];
754         } else {
755                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
756         }
759     # Build return message with twisted target and source
760     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
761     if (defined $forward_to_gosa) {
762       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
763     }
764         &add_content2xml_hash($out_hash, "hostId", "$hostId");
765         &add_content2xml_hash($out_hash, "xxx", "");
767     # Move to XML string
768     $xml_msg= &create_xml_string($out_hash);
769     
770         my $res = &_callOpsi(method=>'getHardwareInformation_hash', params=>[ $hostId ]);
771         if (not &check_opsi_res($res)){
772                 my $result= $res->result;
773                 if (ref $result eq "HASH") {
774                         foreach my $r (keys %{$result}){
775                                 my $item= "\n<item><id>".xml_quote($r)."</id>";
776                                 my $value= $result->{$r};
777                                 foreach my $sres (@{$value}){
779                                         foreach my $dres (keys %{$sres}){
780                                                 if (defined $sres->{$dres}){
781                                                         $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
782                                                 }
783                                         }
785                                 }
786                                 $item.= "</item>";
787                                 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
789                         }
790                 }
791         }
793         $xml_msg=~ s/<xxx><\/xxx>//;
795     # Return message
796         my $endTime = Time::HiRes::time;
797         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
798         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
799     return ( $xml_msg );
802 ################################   
803 # @brief Reports all Opsi clients. 
804 # @param msg - STRING - xml message 
805 # @param msg_hash - HASHREF - message information parsed into a hash
806 # @param session_id - INTEGER - POE session id of the processing of this message
807 # @return out_msg - STRING - feedback to GOsa in success and error case
808 sub opsi_list_clients {
809         my $startTime = Time::HiRes::time;
810     my ($msg, $msg_hash, $session_id) = @_;
811     my $header = @{$msg_hash->{'header'}}[0];
812     my $source = @{$msg_hash->{'source'}}[0];
813     my $target = @{$msg_hash->{'target'}}[0];
814     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
816     # Build return message with twisted target and source
817     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
818     if (defined $forward_to_gosa) {
819       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
820     }
821     &add_content2xml_hash($out_hash, "xxx", "");
823     # Move to XML string
824     my $xml_msg= &create_xml_string($out_hash);
826     # JSON Query
827     my $callobj = {
828         method  => 'getClientsInformation_listOfHashes',
829         params  => [ ],
830         id  => 1,
831     };
833     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
834     if (not &check_opsi_res($res)){
835         foreach my $host (@{$res->result}){
836             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
837             $item.= "<mac>".xml_quote($host->{'macAddress'})."</mac>";
838             if (defined($host->{'description'})){
839                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
840             }
841             if (defined($host->{'notes'})){
842                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
843             }
844             if (defined($host->{'lastSeen'})){
845                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
846             }
848             $item.= "</item>";
849             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
850         }
851     }
852     $xml_msg=~ s/<xxx><\/xxx>//;
854         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
855     return ( $xml_msg );
858 ################################   
859 # @brief Reports client software inventory.
860 # @param msg - STRING - xml message with tag hostId
861 # @param msg_hash - HASHREF - message information parsed into a hash
862 # @param session_id - INTEGER - POE session id of the processing of this message
863 # @return out_msg - STRING - feedback to GOsa in success and error case
864 sub opsi_get_client_software {
865         my $startTime = Time::HiRes::time;
866     my ($msg, $msg_hash, $session_id) = @_;
867     my $header = @{$msg_hash->{'header'}}[0];
868     my $source = @{$msg_hash->{'source'}}[0];
869     my $target = @{$msg_hash->{'target'}}[0];
870     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
871     my $error = 0;
872     my $hostId;
873     my $xml_msg;
875     # Build return message with twisted target and source
876     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
877     if (defined $forward_to_gosa) {
878       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
879     }
881     # Sanity check of needed parameter
882     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
883         $error++;
884         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
885         &add_content2xml_hash($out_hash, "error", "hostId");
886         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
887     }
889     if (not $error) {
891     # Get hostId
892         $hostId = @{$msg_hash->{'hostId'}}[0];
893         &add_content2xml_hash($out_hash, "hostId", "$hostId");
894         &add_content2xml_hash($out_hash, "xxx", "");
895     }
897     $xml_msg= &create_xml_string($out_hash);
899     if (not $error) {
901     # JSON Query
902         my $callobj = {
903             method  => 'getSoftwareInformation_hash',
904             params  => [ $hostId ],
905             id  => 1,
906         };
908         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
909         if (not &check_opsi_res($res)){
910             my $result= $res->result;
911         }
913         $xml_msg=~ s/<xxx><\/xxx>//;
915     }
917     # Return message
918         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
919     return ( $xml_msg );
922 ################################   
923 # @brief Reports product for given hostId or globally.
924 # @param msg - STRING - xml message with optional tag hostId
925 # @param msg_hash - HASHREF - message information parsed into a hash
926 # @param session_id - INTEGER - POE session id of the processing of this message
927 # @return out_msg - STRING - feedback to GOsa in success and error case
928 sub opsi_get_local_products {
929     my $startTime = Time::HiRes::time;
930     my ($msg, $msg_hash, $session_id) = @_;
931     my $header = @{$msg_hash->{'header'}}[0];
932     my $source = @{$msg_hash->{'source'}}[0];
933     my $target = @{$msg_hash->{'target'}}[0];
934     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
935     my $hostId;
937     # Build return message with twisted target and source
938     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
939     if (defined $forward_to_gosa) {
940         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
941     }
942     &add_content2xml_hash($out_hash, "xxx", "");
944     # Get hostId if defined
945     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
946         $hostId = @{$msg_hash->{'hostId'}}[0];
947         &add_content2xml_hash($out_hash, "hostId", $hostId);
948     }
950     my $callobj;
952     # Move to XML string
953     my $xml_msg= &create_xml_string($out_hash);
955     # Check if we need to get host or global information
956     if (defined $hostId){
957       $callobj = {
958           method  => 'getProductHostInformation_list',
959           params  => [ $hostId ],
960           id  => 1,
961       };
963       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
964       if (not &check_opsi_res($res)){
965           foreach my $product (@{$res->result}){
966                my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><state>".xml_quote($product->{'installationStatus'})."</state><action>".xml_quote($product->{'actionRequest'})."</action><\/item><xxx><\/xxx>";
967                $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
968           }
969       }
971     } else {
973       # For hosts, only return the products that are or get installed
974       $callobj = {
975           method  => 'getProductInformation_list',
976           params  => [ undef, 'localboot' ],
977           id  => 1,
978       };
980       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
981       if (not &check_opsi_res($res)){
982           foreach my $product (@{$res->result}) {
983                my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
984                $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
985           }
986       }
987     }
989     $xml_msg=~ s/<xxx><\/xxx>//;
991     # Retrun Message
992         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
993     return ( $xml_msg );
996 ################################   
997 # @brief Deletes a client from Opsi.
998 # @param msg - STRING - xml message with tag hostId
999 # @param msg_hash - HASHREF - message information parsed into a hash
1000 # @param session_id - INTEGER - POE session id of the processing of this message
1001 # @return out_msg - STRING - feedback to GOsa in success and error case
1002 sub opsi_del_client {
1003         my $startTime = Time::HiRes::time;
1004     my ($msg, $msg_hash, $session_id) = @_;
1005     my $header = @{$msg_hash->{'header'}}[0];
1006     my $source = @{$msg_hash->{'source'}}[0];
1007     my $target = @{$msg_hash->{'target'}}[0];
1008     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1009     my $hostId;
1010     my $error = 0;
1012     # Build return message with twisted target and source
1013     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1014     if (defined $forward_to_gosa) {
1015       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1016     }
1018     # Sanity check of needed parameter
1019     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1020         $error++;
1021         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1022         &add_content2xml_hash($out_hash, "error", "hostId");
1023         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1024     }
1026     if (not $error) {
1028     # Get hostId
1029         $hostId = @{$msg_hash->{'hostId'}}[0];
1030         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1032     # JSON Query
1033         my $callobj = {
1034             method  => 'deleteClient',
1035             params  => [ $hostId ],
1036             id  => 1,
1037         };
1038         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1039     }
1041     # Move to XML string
1042     my $xml_msg= &create_xml_string($out_hash);
1044     # Return message
1045         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1046     return ( $xml_msg );
1049 ################################   
1050 # @brief Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1051 # @param msg - STRING - xml message with tags hostId, macaddress
1052 # @param msg_hash - HASHREF - message information parsed into a hash
1053 # @param session_id - INTEGER - POE session id of the processing of this message
1054 # @return out_msg - STRING - feedback to GOsa in success and error case
1055 sub opsi_install_client {
1056         my $startTime = Time::HiRes::time;
1057     my ($msg, $msg_hash, $session_id) = @_;
1058     my $header = @{$msg_hash->{'header'}}[0];
1059     my $source = @{$msg_hash->{'source'}}[0];
1060     my $target = @{$msg_hash->{'target'}}[0];
1061     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1062     my ($hostId, $macaddress);
1063     my $error = 0;
1064     my @out_msg_l;
1066     # Build return message with twisted target and source
1067     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1068     if (defined $forward_to_gosa) {
1069         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1070     }
1072     # Sanity check of needed parameter
1073     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1074         $error++;
1075         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1076         &add_content2xml_hash($out_hash, "error", "hostId");
1077         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1078     }
1079     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
1080         $error++;
1081         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1082         &add_content2xml_hash($out_hash, "error", "macaddress");
1083         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1084     } else {
1085         if ((exists $msg_hash->{'macaddress'}) && 
1086                 ($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)) {  
1087             $macaddress = $1; 
1088         } else { 
1089             $error ++; 
1090             &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1091             &add_content2xml_hash($out_hash, "error", "macaddress");
1092             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1093         }
1094     }
1096     if (not $error) {
1098     # Get hostId
1099         $hostId = @{$msg_hash->{'hostId'}}[0];
1100         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1102         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1103         my $callobj = {
1104             method  => 'getProductStates_hash',
1105             params  => [ $hostId ],
1106             id  => 1,
1107         };
1109         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1110         if (not &check_opsi_res($hres)){
1111             my $htmp= $hres->result->{$hostId};
1113             # check state != not_installed or action == setup -> load and add
1114             foreach my $product (@{$htmp}){
1115                 # Now we've a couple of hashes...
1116                 if ($product->{'installationStatus'} ne "not_installed" or
1117                         $product->{'actionRequest'} ne "none"){
1119                     # Do an action request for all these -> "setup".
1120                     $callobj = {
1121                         method  => 'setProductActionRequest',
1122                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1123                         id  => 1,
1124                     };
1125                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1126                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1127                     if ($res_err){
1128                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1129                     } else {
1130                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1131                     }
1132                 }
1133             }
1134         }
1135         push(@out_msg_l, &create_xml_string($out_hash));
1136     
1138     # Build wakeup message for client
1139         if (not $error) {
1140             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1141             &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1142             my $wakeup_msg = &create_xml_string($wakeup_hash);
1143             push(@out_msg_l, $wakeup_msg);
1145             # invoke trigger wake for this gosa-si-server
1146             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1147         }
1148     }
1149     
1150     # Return messages
1151         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1152     return @out_msg_l;
1155 ################################
1156 # @brief Set action for an Opsi client
1157 # @param product - STRING - Opsi product
1158 # @param action - STRING - action
1159 # @param hostId - STRING - Opsi hostId
1160 sub _set_action {
1161   my $product= shift;
1162   my $action = shift;
1163   my $hostId = shift;
1164   my $callobj;
1166   $callobj = {
1167     method  => 'setProductActionRequest',
1168     params  => [ $product, $hostId, $action],
1169     id  => 1,
1170   };
1172   $main::opsi_client->call($main::opsi_url, $callobj);
1175 ################################
1176 # @brief Set state for an Opsi client
1177 # @param product - STRING - Opsi product
1178 # @param action - STRING - state
1179 # @param hostId - STRING - Opsi hostId
1180 sub _set_state {
1181   my $product = shift;
1182   my $state = shift;
1183   my $hostId = shift;
1184   my $callobj;
1186   $callobj = {
1187     method  => 'setProductState',
1188     params  => [ $product, $hostId, $state ],
1189     id  => 1,
1190   };
1192   $main::opsi_client->call($main::opsi_url, $callobj);
1195 ################################
1196 # @brief Create a license pool at Opsi server.
1197 # @param licensePoolId The name of the pool (optional). 
1198 # @param description The description of the pool (optional).
1199 # @param productIds A list of assigned porducts of the pool (optional). 
1200 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional). 
1201 sub opsi_createLicensePool {
1202         my $startTime = Time::HiRes::time;
1203     my ($msg, $msg_hash, $session_id) = @_;
1204     my $header = @{$msg_hash->{'header'}}[0];
1205     my $source = @{$msg_hash->{'source'}}[0];
1206     my $target = @{$msg_hash->{'target'}}[0];
1207         my $out_hash;
1208         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1209         my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1210         my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1211         my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1213         # Create license Pool
1214     my $callobj = {
1215         method  => 'createLicensePool',
1216         params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1217         id  => 1,
1218     };
1219     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1221         # Check Opsi error
1222         my ($res_error, $res_error_str) = &check_opsi_res($res);
1223         if ($res_error){
1224                 # Create error message
1225                 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1226                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1227                 return ( &create_xml_string($out_hash) );
1228         }
1230         # Create function result message
1231         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1232         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1234         my $endTime = Time::HiRes::time;
1235         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1236         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1237         return ( &create_xml_string($out_hash) );
1240 ################################
1241 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1242 sub opsi_getLicensePools_listOfHashes {
1243         my $startTime = Time::HiRes::time;
1244     my ($msg, $msg_hash, $session_id) = @_;
1245     my $header = @{$msg_hash->{'header'}}[0];
1246     my $source = @{$msg_hash->{'source'}}[0];
1247         my $out_hash;
1249         # Fetch infos from Opsi server
1250     my $callobj = {
1251         method  => 'getLicensePools_listOfHashes',
1252         params  => [ ],
1253         id  => 1,
1254     };
1255     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1257         # Check Opsi error
1258         my ($res_error, $res_error_str) = &check_opsi_res($res);
1259         if ($res_error){
1260                 # Create error message
1261                 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1262                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1263                 return ( &create_xml_string($out_hash) );
1264         }
1266         # Create function result message
1267         my $res_hash = { 'hit'=> [] };
1268         foreach my $licensePool ( @{$res->result}) {
1269                 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1270                         'description' => [$licensePool->{'description'}],
1271                         'productIds' => $licensePool->{'productIds'},
1272                         'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1273                         };
1274                 push( @{$res_hash->{hit}}, $licensePool_hash );
1275         }
1277         # Create function result message
1278         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1279         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1280         $out_hash->{result} = [$res_hash];
1282         my $endTime = Time::HiRes::time;
1283         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1284         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1285         return ( &create_xml_string($out_hash) );
1288 ################################
1289 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1290 # @param licensePoolId The name of the pool. 
1291 sub opsi_getLicensePool_hash {
1292         my $startTime = Time::HiRes::time;
1293     my ($msg, $msg_hash, $session_id) = @_;
1294     my $header = @{$msg_hash->{'header'}}[0];
1295     my $source = @{$msg_hash->{'source'}}[0];
1296     my $target = @{$msg_hash->{'target'}}[0];
1297     my $licensePoolId;
1298         my $out_hash;
1300         # Check input sanity
1301         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1302                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1303         } else {
1304                 return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
1305         }
1307         # Fetch infos from Opsi server
1308     my $callobj = {
1309         method  => 'getLicensePool_hash',
1310         params  => [ $licensePoolId ],
1311         id  => 1,
1312     };
1313     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1315         # Check Opsi error
1316         my ($res_error, $res_error_str) = &check_opsi_res($res);
1317         if ($res_error){
1318                 # Create error message
1319                 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1320                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1321                 &add_content2xml_hash($out_hash, "error", $res_error_str);
1322                 return ( &create_xml_string($out_hash) );
1323         }
1325         # Create function result message
1326         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1327         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1328         &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1329         &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1330         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1331         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1333         my $endTime = Time::HiRes::time;
1334         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1335         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1336         return ( &create_xml_string($out_hash) );
1339 sub _parse_getSoftwareLicenseUsages {
1340         my $res = shift;
1342         # Parse Opsi result
1343         my $tmp_licensePool_cache = {};
1344         my $res_hash = { 'hit'=> [] };
1345         foreach my $license ( @{$res}) {
1346                 my $tmp_licensePool = $license->{'licensePoolId'};
1347                 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1348                         # Fetch missing informations from Opsi and cache the results for a possible later usage
1349                         my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1350                         if (not $err) {
1351                                 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1352                         }
1353                 }
1354                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1355                         'notes' => [$license->{'notes'}],
1356                         'licenseKey' => [$license->{'licenseKey'}],
1357                         'hostId' => [$license->{'hostId'}],
1358                         'licensePoolId' => [$tmp_licensePool],
1359                         };
1360                 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1361                         $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1362                         map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1363                         map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1364                 }
1365                 push( @{$res_hash->{hit}}, $license_hash );
1366         }
1368         return $res_hash;
1371 ################################
1372 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1373 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1374 # @param licensePoolId The name of the pool (optional). 
1375 sub opsi_getSoftwareLicenseUsages {
1376         my $startTime = Time::HiRes::time;
1377         my ($msg, $msg_hash, $session_id) = @_;
1378         my $header = @{$msg_hash->{'header'}}[0];
1379         my $source = @{$msg_hash->{'source'}}[0];
1380         my $target = @{$msg_hash->{'target'}}[0];
1381         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1382         my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1383         my $out_hash;
1385         my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1386         if ($err){
1387                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1388         }
1390         # Parse Opsi result
1391         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1393         # Create function result message
1394         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1395         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1396         $out_hash->{result} = [$res_hash];
1398         my $endTime = Time::HiRes::time;
1399         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1400         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1401         return ( &create_xml_string($out_hash) );
1404 ################################
1405 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1406 # @param productId Something like 'firefox', 'python' or anything else .
1407 sub opsi_getSoftwareLicenseUsagesForProductId {
1408         my $startTime = Time::HiRes::time;
1409         my ($msg, $msg_hash, $session_id) = @_;
1410         my $header = @{$msg_hash->{'header'}}[0];
1411         my $source = @{$msg_hash->{'source'}}[0];
1413         # Check input sanity
1414         my $productId;
1415         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1416                 $productId= @{$msg_hash->{'productId'}}[0];
1417         } else {
1418                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1419         }
1421         # Fetch licensePoolId for productId
1422         my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1423         if ($err){
1424                 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1425         }
1426         my $licensePoolId = $res;   # We assume that there is only one pool for each productID!!!
1428         # Fetch softwareLiceceUsages for licensePoolId
1429         ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1430         if ($err){
1431                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1432         }
1433         # Parse Opsi result
1434         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1436         # Create function result message
1437         my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
1438         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1439         $out_hash->{result} = [$res_hash];
1441         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1442         return ( &create_xml_string($out_hash) );
1445 ################################
1446 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1447 # @param softwareLicenseId Identificator of a license.
1448 sub opsi_getSoftwareLicense_hash {
1449         my $startTime = Time::HiRes::time;
1450         my ($msg, $msg_hash, $session_id) = @_;
1451         my $header = @{$msg_hash->{'header'}}[0];
1452         my $source = @{$msg_hash->{'source'}}[0];
1453         my $target = @{$msg_hash->{'target'}}[0];
1454         my $softwareLicenseId;
1455         my $out_hash;
1457         # Check input sanity
1458         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1459                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1460         } else {
1461                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1462         }
1464         my $callobj = {
1465                 method  => 'getSoftwareLicense_hash',
1466                 params  => [ $softwareLicenseId ],
1467                 id  => 1,
1468         };
1469         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1471         # Check Opsi error
1472         my ($res_error, $res_error_str) = &check_opsi_res($res);
1473         if ($res_error){
1474                 # Create error message
1475                 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1476                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1477                 return ( &create_xml_string($out_hash) );
1478         }
1479         
1480         # Create function result message
1481         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1482         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1483         &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1484         &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1485         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1486         &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1487         foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1488                 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1489                 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1490         }
1492         my $endTime = Time::HiRes::time;
1493         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1494         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1495         return ( &create_xml_string($out_hash) );
1498 ################################
1499 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
1500 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted. 
1501 # @param licensePoolId The name of the pool. 
1502 sub opsi_deleteLicensePool {
1503         my $startTime = Time::HiRes::time;
1504         my ($msg, $msg_hash, $session_id) = @_;
1505     my $header = @{$msg_hash->{'header'}}[0];
1506     my $source = @{$msg_hash->{'source'}}[0];
1507     my $target = @{$msg_hash->{'target'}}[0];
1508     my $licensePoolId;
1509         my $out_hash;
1511         # Check input sanity
1512         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1513                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1514         } else {
1515                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1516         }
1518         # Fetch softwareLicenseIds used in license pool
1519         # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1520         # but not the license contracts of the software licenses. In our case each software license has exactly one license contract. 
1521         my $callobj = {
1522                 method  => 'getSoftwareLicenses_listOfHashes',
1523                 params  => [ ],
1524                 id  => 1,
1525         };
1526         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1528         # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1529         my @lCI_toBeDeleted;
1530         foreach my $softwareLicenseHash ( @{$res->result} ) {
1531                 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) { 
1532                         next; 
1533                 }  
1534                 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1535         }
1537         # Delete license pool at Opsi server
1538     $callobj = {
1539         method  => 'deleteLicensePool',
1540         params  => [ $licensePoolId, 'deleteLicenses=True'  ],
1541         id  => 1,
1542     };
1543     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1544         my ($res_error, $res_error_str) = &check_opsi_res($res);
1545         if ($res_error){
1546                 # Create error message
1547                 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1548                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1549                 return ( &create_xml_string($out_hash) );
1550         } 
1552         # Delete each license contract connected with the license pool
1553         foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1554                 my $callobj = {
1555                         method  => 'deleteLicenseContract',
1556                         params  => [ $licenseContractId ],
1557                         id  => 1,
1558                 };
1559                 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1560                 my ($res_error, $res_error_str) = &check_opsi_res($res);
1561                 if ($res_error){
1562                         # Create error message
1563                         &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1564                         $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1565                         return ( &create_xml_string($out_hash) );
1566                 }
1567         }
1569         # Create function result message
1570         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1571         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1573         my $endTime = Time::HiRes::time;
1574         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1575         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1576         return ( &create_xml_string($out_hash) );
1579 ################################
1580 # @brief Create a license contract, create a software license and add the software license to the license pool
1581 # @param licensePoolId The name of the pool the license should be assigned.
1582 # @param licenseKey The license key.
1583 # @param partner Name of the license partner (optional).
1584 # @param conclusionDate Date of conclusion of license contract (optional)
1585 # @param notificationDate Date of notification that license is running out soon (optional).
1586 # @param notes This is the place for some notes (optional)
1587 # @param softwareLicenseId Identificator of a license (optional).
1588 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1589 # @param maxInstallations The number of clients use this license (optional). 
1590 # @param boundToHost The name of the client the license is bound to (optional).
1591 # @param expirationDate The date when the license is running down (optional). 
1592 sub opsi_createLicense {
1593         my $startTime = Time::HiRes::time;
1594         my ($msg, $msg_hash, $session_id) = @_;
1595     my $header = @{$msg_hash->{'header'}}[0];
1596     my $source = @{$msg_hash->{'source'}}[0];
1597     my $target = @{$msg_hash->{'target'}}[0];
1598         my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1599         my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1600         my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1601         my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1602         my $licenseContractId = undef;
1603         my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1604         my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1605         my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1606         my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1607         my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1608         my $licensePoolId;
1609         my $licenseKey;
1610         my $out_hash;
1612         # Check input sanity
1613         if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1614                 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1615         } else {
1616                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1617         }
1618         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1619                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1620         } else {
1621                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1622         }
1623         if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1624                 return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
1625         }
1626         
1627         # Automatically define licenseContractId if ID is not given
1628         if (defined $softwareLicenseId) { 
1629                 $licenseContractId = "c_".$softwareLicenseId;
1630         }
1632         # Create license contract at Opsi server
1633     my $callobj = {
1634         method  => 'createLicenseContract',
1635         params  => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1636         id  => 1,
1637     };
1638     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1640         # Check Opsi error
1641         my ($res_error, $res_error_str) = &check_opsi_res($res);
1642         if ($res_error){
1643                 # Create error message
1644                 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1645                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1646                 return ( &create_xml_string($out_hash) );
1647         }
1648         
1649         $licenseContractId = $res->result;
1651         # Create software license at Opsi server
1652     $callobj = {
1653         method  => 'createSoftwareLicense',
1654         params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1655         id  => 1,
1656     };
1657     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1659         # Check Opsi error
1660         ($res_error, $res_error_str) = &check_opsi_res($res);
1661         if ($res_error){
1662                 # Create error message
1663                 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1664                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1665                 return ( &create_xml_string($out_hash) );
1666         }
1668         $softwareLicenseId = $res->result;
1670         # Add software license to license pool
1671         $callobj = {
1672         method  => 'addSoftwareLicenseToLicensePool',
1673         params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1674         id  => 1,
1675     };
1676     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1678         # Check Opsi error
1679         ($res_error, $res_error_str) = &check_opsi_res($res);
1680         if ($res_error){
1681                 # Create error message
1682                 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1683                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1684                 return ( &create_xml_string($out_hash) );
1685         }
1687         # Create function result message
1688         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1689         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1690         
1691         my $endTime = Time::HiRes::time;
1692         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1693         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1694         return ( &create_xml_string($out_hash) );
1697 ################################
1698 # @brief Assign a software license to a host
1699 # @param hostid Something like client_1.intranet.mydomain.de
1700 # @param licensePoolId The name of the pool.
1701 sub opsi_assignSoftwareLicenseToHost {
1702         my $startTime = Time::HiRes::time;
1703         my ($msg, $msg_hash, $session_id) = @_;
1704     my $header = @{$msg_hash->{'header'}}[0];
1705     my $source = @{$msg_hash->{'source'}}[0];
1706     my $target = @{$msg_hash->{'target'}}[0];
1707         my $hostId;
1708         my $licensePoolId;
1710         # Check input sanity
1711         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1712                 $hostId = @{$msg_hash->{'hostId'}}[0];
1713         } else {
1714                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1715         }
1716         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1717                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1718         } else {
1719                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1720         }
1722         # Assign a software license to a host
1723         my $callobj = {
1724         method  => 'getAndAssignSoftwareLicenseKey',
1725         params  => [ $hostId, $licensePoolId ],
1726         id  => 1,
1727     };
1728     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1730         # Check Opsi error
1731         my ($res_error, $res_error_str) = &check_opsi_res($res);
1732         if ($res_error){
1733                 # Create error message
1734                 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1735                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1736                 return ( &create_xml_string($out_hash) );
1737         }
1739         # Create function result message
1740         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1741         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1742         
1743         my $endTime = Time::HiRes::time;
1744         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1745         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1746         return ( &create_xml_string($out_hash) );
1749 ################################
1750 # @brief Unassign a software license from a host.
1751 # @param hostid Something like client_1.intranet.mydomain.de
1752 # @param licensePoolId The name of the pool.
1753 sub opsi_unassignSoftwareLicenseFromHost {
1754         my $startTime = Time::HiRes::time;
1755         my ($msg, $msg_hash, $session_id) = @_;
1756     my $header = @{$msg_hash->{'header'}}[0];
1757     my $source = @{$msg_hash->{'source'}}[0];
1758     my $target = @{$msg_hash->{'target'}}[0];
1759         my $hostId;
1760         my $licensePoolId;
1762         # Check input sanity
1763         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1764                 $hostId = @{$msg_hash->{'hostId'}}[0];
1765         } else {
1766                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1767         }
1768         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1769                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1770         } else {
1771                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1772         }
1774         # Unassign a software license from a host
1775         my $callobj = {
1776         method  => 'deleteSoftwareLicenseUsage',
1777         params  => [ $hostId, '', $licensePoolId ],
1778         id  => 1,
1779     };
1780     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1782         # Check Opsi error
1783         my ($res_error, $res_error_str) = &check_opsi_res($res);
1784         if ($res_error){
1785                 # Create error message
1786                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1787                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1788                 return ( &create_xml_string($out_hash) );
1789         }
1791         # Create function result message
1792         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1793         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1794         
1795         my $endTime = Time::HiRes::time;
1796         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1797         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1798         return ( &create_xml_string($out_hash) );
1801 ################################
1802 # @brief Unassign all software licenses from a host
1803 # @param hostid Something like client_1.intranet.mydomain.de
1804 sub opsi_unassignAllSoftwareLicensesFromHost {
1805         my $startTime = Time::HiRes::time;
1806         my ($msg, $msg_hash, $session_id) = @_;
1807     my $header = @{$msg_hash->{'header'}}[0];
1808     my $source = @{$msg_hash->{'source'}}[0];
1809     my $target = @{$msg_hash->{'target'}}[0];
1810         my $hostId;
1812         # Check input sanity
1813         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1814                 $hostId = @{$msg_hash->{'hostId'}}[0];
1815         } else {
1816                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1817         }
1819         # Unassign all software licenses from a host
1820         my $callobj = {
1821         method  => 'deleteAllSoftwareLicenseUsages',
1822         params  => [ $hostId ],
1823         id  => 1,
1824     };
1825     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1827         # Check Opsi error
1828         my ($res_error, $res_error_str) = &check_opsi_res($res);
1829         if ($res_error){
1830                 # Create error message
1831                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1832                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1833                 return ( &create_xml_string($out_hash) );
1834         }
1836         # Create function result message
1837         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1838         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1839         
1840         my $endTime = Time::HiRes::time;
1841         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1842         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1843         return ( &create_xml_string($out_hash) );
1847 ################################
1848 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1849 # and the number of max and remaining installations for a given OPSI product.
1850 # @param productId Identificator of an OPSI product.
1851 sub opsi_getLicenseInformationForProduct {
1852         my $startTime = Time::HiRes::time;
1853     my ($msg, $msg_hash, $session_id) = @_;
1854     my $header = @{$msg_hash->{'header'}}[0];
1855     my $source = @{$msg_hash->{'source'}}[0];
1856         my $productId;
1857         my $out_hash;
1859         # Check input sanity
1860         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1861                 $productId = @{$msg_hash->{'productId'}}[0];
1862         } else {
1863                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1864         }
1866         # Fetch infos from Opsi server
1867     my $callobj = {
1868         method  => 'getLicensePoolId',
1869         params  => [ $productId ],
1870         id  => 1,
1871     };
1872     #my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1873     my $res = $opsi_client->call($opsi_url, $callobj);
1875         # Check Opsi error
1876         my ($res_error, $res_error_str) = &check_opsi_res($res);
1877         if ($res_error){
1878                 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
1879         } 
1880         
1881         my $licensePoolId = $res->result;
1883         # Fetch statistic information for given pool ID
1884         $callobj = {
1885                 method  => 'getLicenseStatistics_hash',
1886                 params  => [ ],
1887                 id  => 1,
1888         };
1889         $res = $opsi_client->call($opsi_url, $callobj);
1891         # Check Opsi error
1892         ($res_error, $res_error_str) = &check_opsi_res($res);
1893         if ($res_error){
1894                 # Create error message
1895                 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
1896                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1897                 return ( &create_xml_string($out_hash) );
1898         }
1900         # Create function result message
1901         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1902         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1903         &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1904         &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
1905         &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
1906         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
1907         &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
1908         map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
1910         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1911         return ( &create_xml_string($out_hash) );
1915 ################################
1916 # @brief Returns licensePoolId, description, a list of productIds, al list of windowsSoftwareIds and a list of licenses for a given licensePoolId. 
1917 # Each license contains softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseKeys, hostIds, expirationDate, boundToHost and licenseContractId.
1918 # The licenseContract contains conclusionDate, expirationDate, notes, notificationDate and partner. 
1919 # @param licensePoolId The name of the pool.
1920 sub opsi_getPool {
1921         my $startTime = Time::HiRes::time;
1922     my ($msg, $msg_hash, $session_id) = @_;
1923     my $header = @{$msg_hash->{'header'}}[0];
1924     my $source = @{$msg_hash->{'source'}}[0];
1926         # Check input sanity
1927         my $licensePoolId;
1928         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1929                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1930         } else {
1931                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1932         }
1934         # Create hash for the answer
1935         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1936         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1938         # Call Opsi
1939         my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
1940         if ($err){
1941                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
1942         }
1943         # Add data to outgoing hash
1944         &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
1945         &add_content2xml_hash($out_hash, "description", $res->{'description'});
1946         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
1947         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
1950         # Call Opsi two times
1951         my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1952         if ($usages_err){
1953                 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
1954         }
1955         my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
1956         if ($licenses_err){
1957                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
1958         }
1960         # Add data to outgoing hash
1961         # Parse through all software licenses and select those associated to the pool
1962         my $res_hash = { 'hit'=> [] };
1963         foreach my $license ( @$licenses_res) {
1964                 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
1965                 my $found = 0;
1966                 my @licensePoolIds_list = @{$license->{licensePoolIds}};
1967                 foreach my $lPI ( @licensePoolIds_list) {
1968                         if ($lPI eq $licensePoolId) { $found++ }
1969                 }
1970                 if (not $found ) { next; };
1971                 # Found matching licensePoolId
1972                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1973                         'licenseKeys' => {},
1974                         'expirationDate' => [$license->{'expirationDate'}],
1975                         'boundToHost' => [$license->{'boundToHost'}],
1976                         'maxInstallations' => [$license->{'maxInstallations'}],
1977                         'licenseType' => [$license->{'licenseType'}],
1978                         'licenseContractId' => [$license->{'licenseContractId'}],
1979                         'licensePoolIds' => [],
1980                         'hostIds' => [],
1981                         };
1982                 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
1983                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
1984                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
1985                 }
1986                 foreach my $usage (@$usages_res) {
1987                         # Search for hostIds with matching softwareLicenseId
1988                         if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
1989                                 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
1990                         }
1991                 }
1993                 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
1994                 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
1995                 if ($lContract_err){
1996                         return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
1997                 }
1998                 $license_hash->{$license->{'licenseContractId'}} = [];
1999                 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
2000                         'notificationDate' => [$lContract_res->{notificationDate}],
2001                         'notes' => [$lContract_res->{notes}],
2002                         'exirationDate' => [$lContract_res->{expirationDate}],
2003                         'partner' => [$lContract_res->{partner}],
2004                 };
2005                 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2007                 push( @{$res_hash->{hit}}, $license_hash );
2008         }
2009         $out_hash->{licenses} = [$res_hash];
2011         my $endTime = Time::HiRes::time;
2012         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2013         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2014     return ( &create_xml_string($out_hash) );
2018 ################################
2019 # @brief Removes at first the software license from license pool and than deletes the software license. 
2020 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2021 # @param softwareLicenseId Identificator of a license.
2022 # @param licensePoolId The name of the pool.
2023 sub opsi_removeLicense {
2024         my $startTime = Time::HiRes::time;
2025     my ($msg, $msg_hash, $session_id) = @_;
2026     my $header = @{$msg_hash->{'header'}}[0];
2027     my $source = @{$msg_hash->{'source'}}[0];
2029         # Check input sanity
2030         my $softwareLicenseId;
2031         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2032                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2033         } else {
2034                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2035         }
2036         my $licensePoolId;
2037         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2038                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2039         } else {
2040                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2041         }
2042         
2043         # Call Opsi
2044         my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2045         if ($err){
2046                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2047         }
2049         # Call Opsi
2050         ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2051         if ($err){
2052                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2053         }
2055         # Create hash for the answer
2056         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2057         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2058         my $endTime = Time::HiRes::time;
2059         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2060         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2061         return ( &create_xml_string($out_hash) );
2065 ################################
2066 # @brief Return softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseContractId, expirationDate, boundToHost and a list of productIds.
2067 # @param hostId Something like client_1.intranet.mydomain.de
2068 sub opsi_getReservedLicenses {
2069         my $startTime = Time::HiRes::time;
2070         my ($msg, $msg_hash, $session_id) = @_;
2071         my $header = @{$msg_hash->{'header'}}[0];
2072         my $source = @{$msg_hash->{'source'}}[0];
2074         # Check input sanity
2075         my $hostId;
2076         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2077                 $hostId = @{$msg_hash->{'hostId'}}[0];
2078         } else {
2079                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2080         }
2082         # Fetch informations from Opsi server
2083         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2084         if ($license_err){
2085                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2086         }
2088         # Parse result
2089         my $res_hash = { 'hit'=> [] };
2090         foreach my $license ( @$license_res) {
2091                 if ($license->{boundToHost} ne $hostId) { next; }
2093                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2094                         'maxInstallations' => [$license->{'maxInstallations'}],
2095                         'boundToHost' => [$license->{'boundToHost'}],
2096                         'expirationDate' => [$license->{'expirationDate'}],
2097                         'licenseContractId' => [$license->{'licenseContractId'}],
2098                         'licenseType' => [$license->{'licenseType'}],
2099                         'licensePoolIds' => [],
2100                         };
2101                 
2102                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2103                         # Fetch information for license pools containing a software license which is bound to given host
2104                         my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2105                         if ($pool_err){
2106                                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2107                         }
2109                         # Add licensePool information to result hash
2110                         push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2111                         $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2112                         map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2113                         map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2114                 }
2115                 push( @{$res_hash->{hit}}, $license_hash );
2116         }
2117         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2118         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2119         $out_hash->{licenses} = [$res_hash];
2121         my $endTime = Time::HiRes::time;
2122         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2123         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2124     return ( &create_xml_string($out_hash) );
2127 ################################
2128 # @brief Bound the given softwareLicenseId to the given host.
2129 # @param hostId Opsi hostId
2130 # @param softwareLicenseId Identificator of a license (optional).
2131 sub opsi_boundHostToLicense {
2132         my $startTime = Time::HiRes::time;
2133         my ($msg, $msg_hash, $session_id) = @_;
2134         my $header = @{$msg_hash->{'header'}}[0];
2135         my $source = @{$msg_hash->{'source'}}[0];
2137         # Check input sanity
2138         my $hostId;
2139         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2140                 $hostId = @{$msg_hash->{'hostId'}}[0];
2141         } else {
2142                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2143         }
2144         my $softwareLicenseId;
2145         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2146                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2147         } else {
2148                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2149         }
2151         # Fetch informations from Opsi server
2152         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2153         if ($license_err){
2154                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2155         }
2157         # Memorize parameter for given softwareLicenseId
2158         my $licenseContractId;
2159         my $licenseType;
2160         my $maxInstallations;
2161         my $boundToHost;
2162         my $expirationDate = "";
2163         my $found;
2164         foreach my $license (@$license_res) {
2165                 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2166                 $licenseContractId = $license->{licenseContractId};
2167                 $licenseType = $license->{licenseType};
2168                 $maxInstallations = $license->{maxInstallations};
2169                 $expirationDate = $license->{expirationDate};
2170                 $found++;
2171         }
2173         if (not $found) {
2174                 return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
2175         }
2177         # Set boundToHost option for a given software license
2178         my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId, 
2179                         'licenseContractId' => $licenseContractId, 
2180                         'licenseType' => $licenseType, 
2181                         'maxInstallations' => $maxInstallations, 
2182                         'boundToHost' => $hostId, 
2183                         'expirationDate' => $expirationDate);
2184         if ($bound_err) {
2185                 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2186         }
2188         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2189         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2191         my $endTime = Time::HiRes::time;
2192         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2193         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2194     return ( &create_xml_string($out_hash) );
2197 ################################
2198 # @brief Release a software license formerly bound to a host.
2199 # @param softwareLicenseId Identificator of a license.
2200 sub opsi_unboundHostFromLicense {
2201         # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2202         # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2203         # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2204         my $startTime = Time::HiRes::time;
2205         my ($msg, $msg_hash, $session_id) = @_;
2206         my $header = @{$msg_hash->{'header'}}[0];
2207         my $source = @{$msg_hash->{'source'}}[0];
2209         # Check input sanity
2210         my $softwareLicenseId;
2211         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2212                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2213         } else {
2214                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2215         }
2216         
2217         # Memorize parameter witch are required for this procedure
2218         my $licenseContractId;
2219         my $licenseType;
2220         my $maxInstallations;
2221         my $expirationDate;
2222         my $partner;
2223         my $conclusionDate;
2224         my $notificationDate;
2225         my $notes;
2226         my $licensePoolId;
2227         my $licenseKey;
2229         # Fetch license informations from Opsi server
2230         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2231         if ($license_err){
2232                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2233         }
2234         my $found = 0;
2235         foreach my $license (@$license_res) {
2236                 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2237                 $licenseContractId = $license->{licenseContractId};
2238                 $licenseType = $license->{licenseType};
2239                 $maxInstallations = $license->{maxInstallations};
2240                 $expirationDate = $license->{expirationDate};
2241                 $licensePoolId = @{$license->{licensePoolIds}}[0];
2242                 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2243                 $found++;
2244         }
2245         
2246         # Fetch contract informations from Opsi server
2247         my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2248         if ($contract_err){
2249                 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2250         }
2251         $partner = $contract_res->{partner};
2252         $conclusionDate = $contract_res->{conclusionDate};
2253         $notificationDate = $contract_res->{notificationDate};
2254         $expirationDate = $contract_res->{expirationDate};
2255         $notes = $contract_res->{notes};
2257         # Delete software license
2258         my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2259         if ($err) {
2260                 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2261         }
2263         # Recreate software license without boundToHost
2264         ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate, 
2265                         'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2266         if ($err) {
2267                 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2268         }
2269         ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType, 
2270                         'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate       );
2271         if ($err) {
2272                 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2273         }
2274         ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2275         if ($err) {
2276                 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2277         }
2279         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2280         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2282         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2283     return ( &create_xml_string($out_hash) );
2286 ################################
2287 # @brief Returns a list of licenses with softwaerLicenseId, maxInstallations, boundToHost, expirationDate, licenseContractId, licenseType, a list of licensePoolIds with associated licenseKeys
2288 sub opsi_getAllSoftwareLicenses {
2289         my $startTime = Time::HiRes::time;
2290         my ($msg, $msg_hash, $session_id) = @_;
2291         my $header = @{$msg_hash->{'header'}}[0];
2292         my $source = @{$msg_hash->{'source'}}[0];
2294         my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
2295         if ($err) {
2296                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
2297         }
2299         # Parse result
2300         my $res_hash = { 'hit'=> [] };
2301         foreach my $license ( @$res) {
2302                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2303                         'maxInstallations' => [$license->{'maxInstallations'}],
2304                         'boundToHost' => [$license->{'boundToHost'}],
2305                         'expirationDate' => [$license->{'expirationDate'}],
2306                         'licenseContractId' => [$license->{'licenseContractId'}],
2307                         'licenseType' => [$license->{'licenseType'}],
2308                         'licensePoolIds' => [],
2309                         'licenseKeys'=> {}
2310                         };
2311                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2312                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2313                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2314                 }
2315                 push( @{$res_hash->{hit}}, $license_hash );
2316         }
2317         
2318         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2319         $out_hash->{licenses} = [$res_hash];
2320         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2322         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2323     return ( &create_xml_string($out_hash) );
2327 ################################
2328 # @brief Returns a list of values for a given host. Values: priority, onceScript, licenseRequired, packageVersion, productVersion, advice, setupScript, windowsSoftwareIds, installationStatus, pxeConfigTemplate, name, creationTimestamp, alwaysScript, productId, description, properties, actionRequest, uninstallScript, action, updateScript and productClassNames 
2329 # @param hostId Opsi hostId
2330 sub opsi_get_full_product_host_information {
2331         my $startTime = Time::HiRes::time;
2332         my ($msg, $msg_hash, $session_id) = @_;
2333         my $header = @{$msg_hash->{'header'}}[0];
2334         my $source = @{$msg_hash->{'source'}}[0];
2336         my ($res, $err) = &_get_full_product_host_information( hostId=>@{$msg_hash->{'hostId'}}[0]);
2337         if ($err) {
2338                 return &_giveErrorFeedback($msg_hash, "cannot fetch full_product_host_information from Opsi server : ".$res, $session_id);
2339         }
2341         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2342         $out_hash->{hit} = $res;
2343         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2345         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2346     return ( &myXmlHashToString($out_hash) );
2350 sub opsi_test {
2351     my ($msg, $msg_hash, $session_id) = @_;
2352     my $header = @{$msg_hash->{'header'}}[0];
2353     my $source = @{$msg_hash->{'source'}}[0];
2354         my $pram1 = @{$msg_hash->{'productId'}}[0];
2357         # Fetch infos from Opsi server
2358     my $callobj = {
2359         method  => 'getLicensePoolId',
2360         params  => [ $pram1 ],
2361         id  => 1,
2362     };
2363     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2365         return ();
2369 # ----------------------------------------------------------------------------
2370 #  internal methods handling the comunication with Opsi
2371 # ----------------------------------------------------------------------------
2373 ################################
2374 # @brief Checks if there is a specified tag and if the the tag has a content.
2375 sub _check_xml_tag_is_ok {
2376         my ($msg_hash,$tag) = @_;
2377         if (not defined $msg_hash->{$tag}) {
2378                 $_ = "message contains no tag '$tag'";
2379                 return 0;
2380         }
2381         if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
2382                 $_ = "message tag '$tag' has no content";
2383                 return  0;
2384         }
2385         return 1;
2388 ################################
2389 # @brief Writes the log line and returns the error message for GOsa.
2390 sub _giveErrorFeedback {
2391         my ($msg_hash, $err_string, $session_id) = @_;
2392         &main::daemon_log("$session_id ERROR: $err_string", 1);
2393         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2394     if (exists $msg_hash->{forward_to_gosa}) {
2395         &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
2396     }
2397         return ( &create_xml_string($out_hash) );
2401 ################################
2402 # @brief Perform the call to the Opsi server and measure the time for the call
2403 sub _callOpsi {
2404         my %arg = ('method'=>undef, 'params'=>[], 'id'=>1, @_);
2406         my $callObject = {
2407                 method => $arg{method},
2408                 params => $arg{params},
2409                 id => $arg{id},
2410         };
2412         my $startTime = Time::HiRes::time;
2413         my $opsiResult = $opsi_client->call($opsi_url, $callObject);
2414         my $endTime = Time::HiRes::time;
2415         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2417         &main::daemon_log("0 DEBUG: time to process opsi call '$arg{method}' : $elapsedTime seconds", 1034); 
2419         return $opsiResult;
2422 sub _getLicensePool_hash {
2423         my %arg = ( 'licensePoolId' => undef, @_ );
2425         if (not defined $arg{licensePoolId} ) { 
2426                 return ("function requires licensePoolId as parameter", 1);
2427         }
2429         my $res = &_callOpsi( method  => 'getLicensePool_hash', params =>[$arg{licensePoolId}], id  => 1 );
2430         my ($res_error, $res_error_str) = &check_opsi_res($res);
2431         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2433         return ($res->result, 0);
2436 sub _getSoftwareLicenses_listOfHashes {
2437         
2438         my $res = &_callOpsi( method  => 'getSoftwareLicenses_listOfHashes' );
2439         my ($res_error, $res_error_str) = &check_opsi_res($res);
2440         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2442         return ($res->result, 0);
2445 sub _getSoftwareLicenseUsages_listOfHashes {
2446         my %arg = ( 'hostId' => "", 'licensePoolId' => "", @_ );
2448         my $res = &_callOpsi( method=>'getSoftwareLicenseUsages_listOfHashes', params=>[ $arg{hostId}, $arg{licensePoolId} ] );
2449         my ($res_error, $res_error_str) = &check_opsi_res($res);
2450         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2452         return ($res->result, 0);
2455 sub _removeSoftwareLicenseFromLicensePool {
2456         my %arg = ( 'softwareLicenseId' => undef, 'licensePoolId' => undef, @_ );
2458         if (not defined $arg{softwareLicenseId} ) { 
2459                 return ("function requires softwareLicenseId as parameter", 1);
2460                 }
2461                 if (not defined $arg{licensePoolId} ) { 
2462                 return ("function requires licensePoolId as parameter", 1);
2463         }
2465         my $res = &_callOpsi( method=>'removeSoftwareLicenseFromLicensePool', params=>[ $arg{softwareLicenseId}, $arg{licensePoolId} ] );
2466         my ($res_error, $res_error_str) = &check_opsi_res($res);
2467         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2469         return ($res->result, 0);
2472 sub _deleteSoftwareLicense {
2473         my %arg = ( 'softwareLicenseId' => undef, 'removeFromPools' => "false", @_ );
2475         if (not defined $arg{softwareLicenseId} ) { 
2476                 return ("function requires softwareLicenseId as parameter", 1);
2477         }
2478         my $removeFromPools = "";
2479         if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) { 
2480                 $removeFromPools = "removeFromPools";
2481         }
2483         my $res = &_callOpsi( method=>'deleteSoftwareLicense', params=>[ $arg{softwareLicenseId}, $removeFromPools ] );
2484         my ($res_error, $res_error_str) = &check_opsi_res($res);
2485         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2487         return ($res->result, 0);
2490 sub _getLicensePoolId {
2491         my %arg = ( 'productId' => undef, @_ );
2492         
2493         if (not defined $arg{productId} ) {
2494                 return ("function requires productId as parameter", 1);
2495         }
2497     my $res = &_callOpsi( method  => 'getLicensePoolId', params  => [ $arg{productId} ] );
2498         my ($res_error, $res_error_str) = &check_opsi_res($res);
2499         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2501         return ($res->result, 0);
2504 sub _getLicenseContract_hash {
2505         my %arg = ( 'licenseContractId' => undef, @_ );
2506         
2507         if (not defined $arg{licenseContractId} ) {
2508                 return ("function requires licenseContractId as parameter", 1);
2509         }
2511     my $res = &_callOpsi( method  => 'getLicenseContract_hash', params  => [ $arg{licenseContractId} ] );
2512         my ($res_error, $res_error_str) = &check_opsi_res($res);
2513         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2515         return ($res->result, 0);
2518 sub _createLicenseContract {
2519         my %arg = (
2520                         'licenseContractId' => undef,
2521                         'partner' => undef,
2522                         'conclusionDate' => undef,
2523                         'notificationDate' => undef,
2524                         'expirationDate' => undef,
2525                         'notes' => undef,
2526                         @_ );
2528         my $res = &_callOpsi( method  => 'createLicenseContract', 
2529                         params  => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2530                         );
2531         my ($res_error, $res_error_str) = &check_opsi_res($res);
2532         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2534         return ($res->result, 0);
2537 sub _createSoftwareLicense {
2538         my %arg = (
2539                         'softwareLicenseId' => undef,
2540                         'licenseContractId' => undef,
2541                         'licenseType' => undef,
2542                         'maxInstallations' => undef,
2543                         'boundToHost' => undef,
2544                         'expirationDate' => undef,
2545                         @_ );
2547     my $res = &_callOpsi( method  => 'createSoftwareLicense',
2548         params  => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2549                 );
2550         my ($res_error, $res_error_str) = &check_opsi_res($res);
2551         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2553         return ($res->result, 0);
2556 sub _addSoftwareLicenseToLicensePool {
2557         my %arg = (
2558             'softwareLicenseId' => undef,
2559             'licensePoolId' => undef,
2560             'licenseKey' => undef,
2561             @_ );
2563         if (not defined $arg{softwareLicenseId} ) {
2564                 return ("function requires softwareLicenseId as parameter", 1);
2565         }
2566         if (not defined $arg{licensePoolId} ) {
2567                 return ("function requires licensePoolId as parameter", 1);
2568         }
2570         my $res = &_callOpsi( method  => 'addSoftwareLicenseToLicensePool', params  => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ] );
2571         my ($res_error, $res_error_str) = &check_opsi_res($res);
2572         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2574         return ($res->result, 0);
2577 sub _getProductStates_hash {
2578         my %arg = (     'hostId' => undef, @_ );
2580         if (not defined $arg{hostId} ) {
2581                 return ("function requires hostId as parameter", 1);
2582         }
2584         my $res = &_callOpsi( method => 'getProductStates_hash', params => [$arg{hostId}]);
2585         my ($res_error, $res_error_str) = &check_opsi_res($res);
2586         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2588         return ($res->result, 0);
2591 sub _get_full_product_host_information {
2592         my %arg = ( 'hostId' => undef, @_ );
2594         if (not defined $arg{hostId}) {
2595                 return ("function requires hostId as parameter", 1);
2596         }
2598         my $res = &_callOpsi( method => 'getFullProductHostInformation_list',  params => [$arg{hostId}]);
2599         my ($res_error, $res_error_str) = &check_opsi_res($res);
2600         if ($res_error){ return ((caller(0))[3]." : ".$res_error_str, 1); }
2602         return ($res->result, 0);
2605 1;