Code

6b9f2b761025b2ab043360734c441354bfd67632
[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 use UNIVERSAL 'isa';
10 @ISA = qw(Exporter);
11 my @events = (
12     "get_events",
13     "opsi_install_client",
14     "opsi_get_netboot_products",  
15     "opsi_get_local_products",
16     "opsi_get_client_hardware",
17     "opsi_get_client_software",
18     "opsi_get_product_properties",
19     "opsi_get_full_product_host_information",
20     "opsi_set_product_properties",
21     "opsi_list_clients",
22     "opsi_del_client",
23     "opsi_add_client",
24     "opsi_modify_client",
25     "opsi_add_product_to_client",
26     "opsi_del_product_from_client",
27     "opsi_createLicensePool",
28     "opsi_deleteLicensePool",
29     "opsi_createLicense",
30     "opsi_assignSoftwareLicenseToHost",
31     "opsi_unassignSoftwareLicenseFromHost",
32     "opsi_unassignAllSoftwareLicensesFromHost",
33     "opsi_getSoftwareLicense_hash",
34     "opsi_getLicensePool_hash",
35     "opsi_getSoftwareLicenseUsages",
36     "opsi_getSoftwareLicenseUsagesForProductId",
37     "opsi_getLicensePools_listOfHashes",
38     "opsi_getLicenseInformationForProduct",
39     "opsi_getPool",
40     "opsi_getAllSoftwareLicenses",
41     "opsi_removeLicense",
42     "opsi_getReservedLicenses",
43     "opsi_boundHostToLicense",
44     "opsi_unboundHostFromLicense",
45     "opsi_test",
46    );
47 @EXPORT = @events;
49 use strict;
50 use warnings;
51 use GOSA::GosaSupportDaemon;
52 use Data::Dumper;
53 use XML::Quote qw(:all);
55 BEGIN {}
57 END {}
59 # ----------------------------------------------------------------------------
60 #                          D E C L A R A T I O N S
61 # ----------------------------------------------------------------------------
63 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
64 my ($opsi_enabled, $opsi_server, $opsi_admin, $opsi_password, $opsi_url, $opsi_client);
65 my %cfg_defaults = (
66                 "Opsi" => {
67                 "enabled"  => [\$opsi_enabled, "false"],
68                 "server"   => [\$opsi_server, "localhost"],
69                 "admin"    => [\$opsi_admin, "opsi-admin"],
70                 "password" => [\$opsi_password, "secret"],
71                 },
72 );
73 &read_configfile($main::cfg_file, %cfg_defaults);
74 if ($opsi_enabled eq "true") {
75         use JSON::RPC::Client;
76         use XML::Quote qw(:all);
77         use Time::HiRes qw( time );
78         $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
79         $opsi_client = new JSON::RPC::Client;
81         # Check version dependencies
82         eval { &myXmlHashToString(); };
83         if ($@ ) {
84                 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";
85         }
86 }
88 # ----------------------------------------------------------------------------
89 #   external methods handling the comunication with GOsa/GOsa-si
90 # ----------------------------------------------------------------------------
92 ################################
93 # @brief A function returning a list of functions which are exported by importing the module.
94 # @return List of all provided functions
95 sub get_events {
96     return \@events;
97 }
99 ################################
100 # @brief Adds an Opsi product to an Opsi client.
101 # @param msg - STRING - xml message with tags hostId and productId
102 # @param msg_hash - HASHREF - message information parsed into a hash
103 # @param session_id - INTEGER - POE session id of the processing of this message
104 # @return out_msg - STRING - feedback to GOsa in success and error case
105 sub opsi_add_product_to_client {
106         my $startTime = Time::HiRes::time;
107     my ($msg, $msg_hash, $session_id) = @_;
108     my $header = @{$msg_hash->{'header'}}[0];
109     my $source = @{$msg_hash->{'source'}}[0];
110     my $target = @{$msg_hash->{'target'}}[0];
111     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
113     # Build return message
114     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
115     if (defined $forward_to_gosa) {
116         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
117     }
119     # Sanity check of needed parameter
120     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
121                 return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
122     }
123     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
124                 return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
125     }
127         # Get hostId
128         my $hostId = @{$msg_hash->{'hostId'}}[0];
129         &add_content2xml_hash($out_hash, "hostId", $hostId);
131         # Get productID
132         my $productId = @{$msg_hash->{'productId'}}[0];
133         &add_content2xml_hash($out_hash, "productId", $productId);
135         # Do an action request for all these -> "setup".
136         my $callobj = {
137                 method  => 'setProductActionRequest',
138                 params  => [ $productId, $hostId, "setup" ],
139                 id  => 1, }; 
140         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
142         if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
144         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
145     return ( &create_xml_string($out_hash) );
148 ################################
149 # @brief Deletes an Opsi-product from an Opsi-client. 
150 # @param msg - STRING - xml message with tags hostId and productId
151 # @param msg_hash - HASHREF - message information parsed into a hash
152 # @param session_id - INTEGER - POE session id of the processing of this message
153 # @return out_msg - STRING - feedback to GOsa in success and error case
154 sub opsi_del_product_from_client {
155         my $startTime = Time::HiRes::time;
156     my ($msg, $msg_hash, $session_id) = @_;
157     my $header = @{$msg_hash->{'header'}}[0];
158     my $source = @{$msg_hash->{'source'}}[0];
159     my $target = @{$msg_hash->{'target'}}[0];
160     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
161     my ($hostId, $productId);
162     my $error = 0;
163     my ($sres, $sres_err, $sres_err_string);
165     # Build return message
166     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
167     if (defined $forward_to_gosa) {
168         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
169     }
171     # Sanity check of needed parameter
172     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
173         $error++;
174         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
175         &add_content2xml_hash($out_hash, "error", "hostId");
176         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
178     }
179     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
180         $error++;
181         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
182         &add_content2xml_hash($out_hash, "error", "productId");
183         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
184     }
186     # All parameter available
187     if (not $error) {
188         # Get hostId
189         $hostId = @{$msg_hash->{'hostId'}}[0];
190         &add_content2xml_hash($out_hash, "hostId", $hostId);
192         # Get productID
193         $productId = @{$msg_hash->{'productId'}}[0];
194         &add_content2xml_hash($out_hash, "productId", $productId);
196         # Check to get product action list 
197         my $callobj = {
198             method  => 'getPossibleProductActions_list',
199             params  => [ $productId ],
200             id  => 1, };
201         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
202         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
203         if ($sres_err){
204             &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
205             &add_content2xml_hash($out_hash, "error", $sres_err_string);
206             $error++;
207         }
208     }
210     # Check action uninstall of product
211     if (not $error) {
212         my $uninst_possible= 0;
213         foreach my $r (@{$sres->result}) {
214             if ($r eq 'uninstall') {
215                 $uninst_possible= 1;
216             }
217         }
218         if (!$uninst_possible){
219             &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
220             &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
221             $error++;
222         }
223     }
225     # Set product state to "none"
226     # Do an action request for all these -> "setup".
227     if (not $error) {
228         my $callobj = {
229             method  => 'setProductActionRequest',
230             params  => [ $productId, $hostId, "none" ],
231             id  => 1, 
232         }; 
233         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
234         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
235         if ($sres_err){
236             &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
237             &add_content2xml_hash($out_hash, "error", $sres_err_string);
238         }
239     }
241     # Return message
242         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
243     return ( &create_xml_string($out_hash) );
246 ################################
247 # @brief Adds an Opsi client to Opsi.
248 # @param msg - STRING - xml message with tags hostId and macaddress
249 # @param msg_hash - HASHREF - message information parsed into a hash
250 # @param session_id - INTEGER - POE session id of the processing of this message
251 # @return out_msg - STRING - feedback to GOsa in success and error case
252 sub opsi_add_client {
253         my $startTime = Time::HiRes::time;
254     my ($msg, $msg_hash, $session_id) = @_;
255     my $header = @{$msg_hash->{'header'}}[0];
256     my $source = @{$msg_hash->{'source'}}[0];
257     my $target = @{$msg_hash->{'target'}}[0];
258     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
259     my ($hostId, $mac);
260     my $error = 0;
261     my ($sres, $sres_err, $sres_err_string);
263     # Build return message with twisted target and source
264     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
265     if (defined $forward_to_gosa) {
266         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
267     }
269     # Sanity check of needed parameter
270     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
271         $error++;
272         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
273         &add_content2xml_hash($out_hash, "error", "hostId");
274         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
275     }
276     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
277         $error++;
278         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
279         &add_content2xml_hash($out_hash, "error", "macaddress");
280         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
281     }
283     if (not $error) {
284         # Get hostId
285         $hostId = @{$msg_hash->{'hostId'}}[0];
286         &add_content2xml_hash($out_hash, "hostId", $hostId);
288         # Get macaddress
289         $mac = @{$msg_hash->{'macaddress'}}[0];
290         &add_content2xml_hash($out_hash, "macaddress", $mac);
292         my $name= $hostId;
293         $name=~ s/^([^.]+).*$/$1/;
294         my $domain= $hostId;
295         $domain=~ s/^[^.]+\.(.*)$/$1/;
296         my ($description, $notes, $ip);
298         if (defined @{$msg_hash->{'description'}}[0]){
299             $description = @{$msg_hash->{'description'}}[0];
300         }
301         if (defined @{$msg_hash->{'notes'}}[0]){
302             $notes = @{$msg_hash->{'notes'}}[0];
303         }
304         if (defined @{$msg_hash->{'ip'}}[0]){
305             $ip = @{$msg_hash->{'ip'}}[0];
306         }
308         my $callobj;
309         $callobj = {
310             method  => 'createClient',
311             params  => [ $name, $domain, $description, $notes, $ip, $mac ],
312             id  => 1,
313         };
315         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
316         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
317         if ($sres_err){
318             &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
319             &add_content2xml_hash($out_hash, "error", $sres_err_string);
320         } else {
321             &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5); 
322         }
323     }
325     # Return message
326         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
327     return ( &create_xml_string($out_hash) );
330 ################################
331 # @brief Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
332 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
333 # @param msg_hash - HASHREF - message information parsed into a hash
334 # @param session_id - INTEGER - POE session id of the processing of this message    
335 # @return out_msg - STRING - feedback to GOsa in success and error case
336 sub opsi_modify_client {
337         my $startTime = Time::HiRes::time;
338     my ($msg, $msg_hash, $session_id) = @_;
339     my $header = @{$msg_hash->{'header'}}[0];
340     my $source = @{$msg_hash->{'source'}}[0];
341     my $target = @{$msg_hash->{'target'}}[0];
342     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
343     my $hostId;
344     my $error = 0;
345     my ($sres, $sres_err, $sres_err_string);
347     # Build return message with twisted target and source
348     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
349     if (defined $forward_to_gosa) {
350         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
351     }
353     # Sanity check of needed parameter
354     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
355         $error++;
356         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
357         &add_content2xml_hash($out_hash, "error", "hostId");
358         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
359     }
361     if (not $error) {
362         # Get hostId
363         $hostId = @{$msg_hash->{'hostId'}}[0];
364         &add_content2xml_hash($out_hash, "hostId", $hostId);
365         my $name= $hostId;
366         $name=~ s/^([^.]+).*$/$1/;
367         my $domain= $hostId;
368         $domain=~ s/^[^.]+(.*)$/$1/;
370         # Modify description, notes or mac if defined
371         my ($description, $notes, $mac);
372         my $callobj;
373         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
374             $description = @{$msg_hash->{'description'}}[0];
375             $callobj = {
376                 method  => 'setHostDescription',
377                 params  => [ $hostId, $description ],
378                 id  => 1,
379             };
380             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
381             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
382             if ($sres_err){
383                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
384                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
385             }
386         }
387         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
388             $notes = @{$msg_hash->{'notes'}}[0];
389             $callobj = {
390                 method  => 'setHostNotes',
391                 params  => [ $hostId, $notes ],
392                 id  => 1,
393             };
394             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
395             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
396             if ($sres_err){
397                 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
398                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
399             }
400         }
401         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
402             $mac = @{$msg_hash->{'mac'}}[0];
403             $callobj = {
404                 method  => 'setMacAddress',
405                 params  => [ $hostId, $mac ],
406                 id  => 1,
407             };
408             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
409             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
410             if ($sres_err){
411                 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
412                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
413             }
414         }
415     }
417     # Return message
418         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
419     return ( &create_xml_string($out_hash) );
421  
422 ################################
423 # @brief Get netboot products for specific host.
424 # @param msg - STRING - xml message with tag hostId
425 # @param msg_hash - HASHREF - message information parsed into a hash
426 # @param session_id - INTEGER - POE session id of the processing of this message
427 # @return out_msg - STRING - feedback to GOsa in success and error case
428 sub opsi_get_netboot_products {
429     my $startTime = Time::HiRes::time;
430     my ($msg, $msg_hash, $session_id) = @_;
431     my $header = @{$msg_hash->{'header'}}[0];
432     my $source = @{$msg_hash->{'source'}}[0];
433     my $target = @{$msg_hash->{'target'}}[0];
434     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
435     my $hostId;
437     # Build return message with twisted target and source
438     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
439     if (defined $forward_to_gosa) {
440         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
441     }
442     &add_content2xml_hash($out_hash, "xxx", "");
444     # Get hostId if defined
445     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
446         $hostId = @{$msg_hash->{'hostId'}}[0];
447         &add_content2xml_hash($out_hash, "hostId", $hostId);
448     }
450     # Move to XML string
451     my $xml_msg= &create_xml_string($out_hash);
453     my $callobj;
454     # Check if we need to get host or global information
455     if (defined $hostId){
456       $callobj = {
457           method  => 'getProductHostInformation_list',
458           params  => [ $hostId, undef, 'netboot'],
459           id  => 1,
460       };
462       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
463       if (not &check_opsi_res($res)){
464           foreach my $product (@{$res->result}){
465                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>";
466                $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
467           }
468       }
470     } else {
472       # For hosts, only return the products that are or get installed
473       $callobj = {
474           method  => 'getProductInformation_list',
475           params  => [ undef, 'netboot' ],
476           id  => 1,
477       };
479       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
480       if (not &check_opsi_res($res)){
481           foreach my $product (@{$res->result}) {
482                my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
483                $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
484           }
485       }
486     }
488     $xml_msg=~ s/<xxx><\/xxx>//;
490     # Retrun Message
491         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
492     return ( $xml_msg );
495 ################################   
496 # @brief Get product properties for a product and a specific host or gobally for a product.
497 # @param msg - STRING - xml message with tags productId and optional hostId
498 # @param msg_hash - HASHREF - message information parsed into a hash
499 # @param session_id - INTEGER - POE session id of the processing of this message
500 # @return out_msg - STRING - feedback to GOsa in success and error case
501 sub opsi_get_product_properties {
502         my $startTime = Time::HiRes::time;
503     my ($msg, $msg_hash, $session_id) = @_;
504     my $header = @{$msg_hash->{'header'}}[0];
505     my $source = @{$msg_hash->{'source'}}[0];
506     my $target = @{$msg_hash->{'target'}}[0];
507     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
508     my ($hostId, $productId);
509     my $xml_msg;
511     # Build return message with twisted target and source
512     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
513     if (defined $forward_to_gosa) {
514         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
515     }
517     # Sanity check of needed parameter
518     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
519         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
520         &add_content2xml_hash($out_hash, "error", "productId");
521         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
523         # Return message
524         return ( &create_xml_string($out_hash) );
525     }
527     # Get productid
528     $productId = @{$msg_hash->{'productId'}}[0];
529     &add_content2xml_hash($out_hash, "producId", "$productId");
531     # Get hostId if defined
532     if (defined @{$msg_hash->{'hostId'}}[0]){
533       $hostId = @{$msg_hash->{'hostId'}}[0];
534       &add_content2xml_hash($out_hash, "hostId", $hostId);
535     }
537     # Load actions
538     my $callobj = {
539       method  => 'getPossibleProductActions_list',
540       params  => [ $productId ],
541       id  => 1,
542     };
543     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
544     if (not &check_opsi_res($res)){
545       foreach my $action (@{$res->result}){
546         &add_content2xml_hash($out_hash, "action", $action);
547       }
548     }
550     # Add place holder
551     &add_content2xml_hash($out_hash, "xxx", "");
553     # Move to XML string
554     $xml_msg= &create_xml_string($out_hash);
556     # JSON Query
557     if (defined $hostId){
558       $callobj = {
559           method  => 'getProductProperties_hash',
560           params  => [ $productId, $hostId ],
561           id  => 1,
562       };
563     } else {
564       $callobj = {
565           method  => 'getProductProperties_hash',
566           params  => [ $productId ],
567           id  => 1,
568       };
569     }
570     $res = $main::opsi_client->call($main::opsi_url, $callobj);
572     # JSON Query 2
573     $callobj = {
574       method  => 'getProductPropertyDefinitions_listOfHashes',
575       params  => [ $productId ],
576       id  => 1,
577     };
579     # Assemble options
580     my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
581     my $values = {};
582     my $descriptions = {};
583     if (not &check_opsi_res($res2)){
584         my $r= $res2->result;
586           foreach my $entr (@$r){
587             # Unroll values
588             my $cnv;
589             if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
590               foreach my $v (@{$entr->{'values'}}){
591                 $cnv.= "<value>$v</value>";
592               }
593             } else {
594               $cnv= $entr->{'values'};
595             }
596             $values->{$entr->{'name'}}= $cnv;
597             $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
598           }
599     }
601     if (not &check_opsi_res($res)){
602         my $r= $res->result;
603         foreach my $key (keys %{$r}) {
604             my $item= "\n<item>";
605             my $value= $r->{$key};
606             my $dsc= "";
607             my $vals= "";
608             if (defined $descriptions->{$key}){
609               $dsc= $descriptions->{$key};
610             }
611             if (defined $values->{$key}){
612               $vals= $values->{$key};
613             }
614             $item.= "<$key>$dsc<current>".xml_quote($value)."</current>$vals</$key>";
615             $item.= "</item>";
616             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
617         }
618     }
620     $xml_msg=~ s/<xxx><\/xxx>//;
622     # Return message
623         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
624     return ( $xml_msg );
627 ################################   
628 # @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.
629 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
630 # @param msg_hash - HASHREF - message information parsed into a hash
631 # @param session_id - INTEGER - POE session id of the processing of this message
632 # @return out_msg - STRING - feedback to GOsa in success and error case
633 sub opsi_set_product_properties {
634         my $startTime = Time::HiRes::time;
635     my ($msg, $msg_hash, $session_id) = @_;
636     my $header = @{$msg_hash->{'header'}}[0];
637     my $source = @{$msg_hash->{'source'}}[0];
638     my $target = @{$msg_hash->{'target'}}[0];
639     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
640     my ($productId, $hostId);
642     # Build return message with twisted target and source
643     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
644     if (defined $forward_to_gosa) {
645         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
646     }
648     # Sanity check of needed parameter
649     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
650         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
651         &add_content2xml_hash($out_hash, "error", "productId");
652         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
653         return ( &create_xml_string($out_hash) );
654     }
655     if (not exists $msg_hash->{'item'}) {
656         &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
657         &add_content2xml_hash($out_hash, "error", "item");
658         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
659         return ( &create_xml_string($out_hash) );
660     } else {
661         if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
662             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
663             &add_content2xml_hash($out_hash, "error", "name");
664             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
665             return ( &create_xml_string($out_hash) );
666         }
667         if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
668             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
669             &add_content2xml_hash($out_hash, "error", "value");
670             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
671             return ( &create_xml_string($out_hash) );
672         }
673     }
674     # if no hostId is given, set_product_properties will act on globally
675     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
676         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
677         &add_content2xml_hash($out_hash, "error", "hostId");
678         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
679         return ( &create_xml_string($out_hash) );
680     }
682         
683     # Get productId
684     $productId =  @{$msg_hash->{'productId'}}[0];
685     &add_content2xml_hash($out_hash, "productId", $productId);
687     # Get hostId if defined
688     if (exists $msg_hash->{'hostId'}){
689         $hostId = @{$msg_hash->{'hostId'}}[0];
690         &add_content2xml_hash($out_hash, "hostId", $hostId);
691     }
693     # Set product states if requested
694     if (defined @{$msg_hash->{'action'}}[0]){
695         &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
696     }
697     if (defined @{$msg_hash->{'state'}}[0]){
698         &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
699     }
701     # Find properties
702     foreach my $item (@{$msg_hash->{'item'}}){
703         # JSON Query
704         my $callobj;
706         if (defined $hostId){
707             $callobj = {
708                 method  => 'setProductProperty',
709                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
710                 id  => 1,
711             };
712         } else {
713             $callobj = {
714                 method  => 'setProductProperty',
715                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
716                 id  => 1,
717             };
718         }
720         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
721         my ($res_err, $res_err_string) = &check_opsi_res($res);
723         if ($res_err){
724             &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
725             &add_content2xml_hash($out_hash, "error", $res_err_string);
726         }
727     }
730     # Return message
731         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
732     return ( &create_xml_string($out_hash) );
735 ################################   
736 # @brief Reports client hardware inventory.
737 # @param msg - STRING - xml message with tag hostId
738 # @param msg_hash - HASHREF - message information parsed into a hash
739 # @param session_id - INTEGER - POE session id of the processing of this message
740 # @return out_msg - STRING - feedback to GOsa in success and error case
741 sub opsi_get_client_hardware {
742         my $startTime = Time::HiRes::time;
743     my ($msg, $msg_hash, $session_id) = @_;
744     my $header = @{$msg_hash->{'header'}}[0];
745     my $source = @{$msg_hash->{'source'}}[0];
746     my $target = @{$msg_hash->{'target'}}[0];
747     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
748     my $hostId;
749     my $error = 0;
750     my $xml_msg;
752     # Sanity check of needed parameter
753         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
754         $hostId = @{$msg_hash->{'hostId'}}[0];
755         } else {
756                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
757         }
760     # Build return message with twisted target and source
761     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
762     if (defined $forward_to_gosa) {
763       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
764     }
765         &add_content2xml_hash($out_hash, "hostId", "$hostId");
766         &add_content2xml_hash($out_hash, "xxx", "");
768     # Move to XML string
769     $xml_msg= &create_xml_string($out_hash);
770     
771         my $res = &_callOpsi(method=>'getHardwareInformation_hash', params=>[ $hostId ]);
772         if (not &check_opsi_res($res)){
773                 my $result= $res->result;
774                 if (ref $result eq "HASH") {
775                         foreach my $r (keys %{$result}){
776                                 my $item= "\n<item><id>".xml_quote($r)."</id>";
777                                 my $value= $result->{$r};
778                                 foreach my $sres (@{$value}){
780                                         foreach my $dres (keys %{$sres}){
781                                                 if (defined $sres->{$dres}){
782                                                         $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
783                                                 }
784                                         }
786                                 }
787                                 $item.= "</item>";
788                                 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
790                         }
791                 }
792         }
794         $xml_msg=~ s/<xxx><\/xxx>//;
796     # Return message
797         my $endTime = Time::HiRes::time;
798         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
799         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
800     return ( $xml_msg );
803 ################################   
804 # @brief Reports all Opsi clients. 
805 # @param msg - STRING - xml message 
806 # @param msg_hash - HASHREF - message information parsed into a hash
807 # @param session_id - INTEGER - POE session id of the processing of this message
808 # @return out_msg - STRING - feedback to GOsa in success and error case
809 sub opsi_list_clients {
810         my $startTime = Time::HiRes::time;
811     my ($msg, $msg_hash, $session_id) = @_;
812     my $header = @{$msg_hash->{'header'}}[0];
813     my $source = @{$msg_hash->{'source'}}[0];
814     my $target = @{$msg_hash->{'target'}}[0];
815     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
817     # Build return message with twisted target and source
818     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
819     if (defined $forward_to_gosa) {
820       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
821     }
822     &add_content2xml_hash($out_hash, "xxx", "");
824     # Move to XML string
825     my $xml_msg= &create_xml_string($out_hash);
827     # JSON Query
828     my $callobj = {
829         method  => 'getClientsInformation_listOfHashes',
830         params  => [ ],
831         id  => 1,
832     };
834     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
835     if (not &check_opsi_res($res)){
836         foreach my $host (@{$res->result}){
837             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
838             $item.= "<mac>".xml_quote($host->{'macAddress'})."</mac>";
839             if (defined($host->{'description'})){
840                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
841             }
842             if (defined($host->{'notes'})){
843                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
844             }
845             if (defined($host->{'lastSeen'})){
846                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
847             }
849             $item.= "</item>";
850             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
851         }
852     }
853     $xml_msg=~ s/<xxx><\/xxx>//;
855         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
856     return ( $xml_msg );
859 ################################   
860 # @brief Reports client software inventory.
861 # @param msg - STRING - xml message with tag hostId
862 # @param msg_hash - HASHREF - message information parsed into a hash
863 # @param session_id - INTEGER - POE session id of the processing of this message
864 # @return out_msg - STRING - feedback to GOsa in success and error case
865 sub opsi_get_client_software {
866         my $startTime = Time::HiRes::time;
867     my ($msg, $msg_hash, $session_id) = @_;
868     my $header = @{$msg_hash->{'header'}}[0];
869     my $source = @{$msg_hash->{'source'}}[0];
870     my $target = @{$msg_hash->{'target'}}[0];
871     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
872     my $error = 0;
873     my $hostId;
874     my $xml_msg;
876     # Build return message with twisted target and source
877     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
878     if (defined $forward_to_gosa) {
879       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
880     }
882     # Sanity check of needed parameter
883     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
884         $error++;
885         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
886         &add_content2xml_hash($out_hash, "error", "hostId");
887         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
888     }
890     if (not $error) {
892     # Get hostId
893         $hostId = @{$msg_hash->{'hostId'}}[0];
894         &add_content2xml_hash($out_hash, "hostId", "$hostId");
895         &add_content2xml_hash($out_hash, "xxx", "");
896     }
898     $xml_msg= &create_xml_string($out_hash);
900     if (not $error) {
902     # JSON Query
903         my $callobj = {
904             method  => 'getSoftwareInformation_hash',
905             params  => [ $hostId ],
906             id  => 1,
907         };
909         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
910         if (not &check_opsi_res($res)){
911             my $result= $res->result;
912         }
914         $xml_msg=~ s/<xxx><\/xxx>//;
916     }
918     # Return message
919         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
920     return ( $xml_msg );
923 ################################   
924 # @brief Reports product for given hostId or globally.
925 # @param msg - STRING - xml message with optional tag hostId
926 # @param msg_hash - HASHREF - message information parsed into a hash
927 # @param session_id - INTEGER - POE session id of the processing of this message
928 # @return out_msg - STRING - feedback to GOsa in success and error case
929 sub opsi_get_local_products {
930     my $startTime = Time::HiRes::time;
931     my ($msg, $msg_hash, $session_id) = @_;
932     my $header = @{$msg_hash->{'header'}}[0];
933     my $source = @{$msg_hash->{'source'}}[0];
934     my $target = @{$msg_hash->{'target'}}[0];
935     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
936     my $hostId;
938     # Build return message with twisted target and source
939     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
940     if (defined $forward_to_gosa) {
941         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
942     }
943     &add_content2xml_hash($out_hash, "xxx", "");
945     # Get hostId if defined
946     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
947         $hostId = @{$msg_hash->{'hostId'}}[0];
948         &add_content2xml_hash($out_hash, "hostId", $hostId);
949     }
951     my $callobj;
953     # Move to XML string
954     my $xml_msg= &create_xml_string($out_hash);
956     # Check if we need to get host or global information
957     if (defined $hostId){
958       $callobj = {
959           method  => 'getProductHostInformation_list',
960           params  => [ $hostId ],
961           id  => 1,
962       };
964       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
965       if (not &check_opsi_res($res)){
966           foreach my $product (@{$res->result}){
967                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>";
968                $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
969           }
970       }
972     } else {
974       # For hosts, only return the products that are or get installed
975       $callobj = {
976           method  => 'getProductInformation_list',
977           params  => [ undef, 'localboot' ],
978           id  => 1,
979       };
981       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
982       if (not &check_opsi_res($res)){
983           foreach my $product (@{$res->result}) {
984                my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
985                $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
986           }
987       }
988     }
990     $xml_msg=~ s/<xxx><\/xxx>//;
992     # Retrun Message
993         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
994     return ( $xml_msg );
997 ################################   
998 # @brief Deletes a client from Opsi.
999 # @param msg - STRING - xml message with tag hostId
1000 # @param msg_hash - HASHREF - message information parsed into a hash
1001 # @param session_id - INTEGER - POE session id of the processing of this message
1002 # @return out_msg - STRING - feedback to GOsa in success and error case
1003 sub opsi_del_client {
1004         my $startTime = Time::HiRes::time;
1005     my ($msg, $msg_hash, $session_id) = @_;
1006     my $header = @{$msg_hash->{'header'}}[0];
1007     my $source = @{$msg_hash->{'source'}}[0];
1008     my $target = @{$msg_hash->{'target'}}[0];
1009     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1010     my $hostId;
1011     my $error = 0;
1013     # Build return message with twisted target and source
1014     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1015     if (defined $forward_to_gosa) {
1016       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1017     }
1019     # Sanity check of needed parameter
1020     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1021         $error++;
1022         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1023         &add_content2xml_hash($out_hash, "error", "hostId");
1024         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1025     }
1027     if (not $error) {
1029     # Get hostId
1030         $hostId = @{$msg_hash->{'hostId'}}[0];
1031         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1033     # JSON Query
1034         my $callobj = {
1035             method  => 'deleteClient',
1036             params  => [ $hostId ],
1037             id  => 1,
1038         };
1039         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1040     }
1042     # Move to XML string
1043     my $xml_msg= &create_xml_string($out_hash);
1045     # Return message
1046         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1047     return ( $xml_msg );
1050 ################################   
1051 # @brief Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1052 # @param msg - STRING - xml message with tags hostId, macaddress
1053 # @param msg_hash - HASHREF - message information parsed into a hash
1054 # @param session_id - INTEGER - POE session id of the processing of this message
1055 # @return out_msg - STRING - feedback to GOsa in success and error case
1056 sub opsi_install_client {
1057         my $startTime = Time::HiRes::time;
1058     my ($msg, $msg_hash, $session_id) = @_;
1059     my $header = @{$msg_hash->{'header'}}[0];
1060     my $source = @{$msg_hash->{'source'}}[0];
1061     my $target = @{$msg_hash->{'target'}}[0];
1062     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1063     my ($hostId, $macaddress);
1064     my $error = 0;
1065     my @out_msg_l;
1067     # Build return message with twisted target and source
1068     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1069     if (defined $forward_to_gosa) {
1070         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1071     }
1073     # Sanity check of needed parameter
1074     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1075         $error++;
1076         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1077         &add_content2xml_hash($out_hash, "error", "hostId");
1078         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1079     }
1080     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
1081         $error++;
1082         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1083         &add_content2xml_hash($out_hash, "error", "macaddress");
1084         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1085     } else {
1086         if ((exists $msg_hash->{'macaddress'}) && 
1087                 ($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)) {  
1088             $macaddress = $1; 
1089         } else { 
1090             $error ++; 
1091             &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1092             &add_content2xml_hash($out_hash, "error", "macaddress");
1093             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1094         }
1095     }
1097     if (not $error) {
1099     # Get hostId
1100         $hostId = @{$msg_hash->{'hostId'}}[0];
1101         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1103         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1104         my $callobj = {
1105             method  => 'getProductStates_hash',
1106             params  => [ $hostId ],
1107             id  => 1,
1108         };
1110         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1111         if (not &check_opsi_res($hres)){
1112             my $htmp= $hres->result->{$hostId};
1114             # check state != not_installed or action == setup -> load and add
1115             foreach my $product (@{$htmp}){
1116                 # Now we've a couple of hashes...
1117                 if ($product->{'installationStatus'} ne "not_installed" or
1118                         $product->{'actionRequest'} ne "none"){
1120                     # Do an action request for all these -> "setup".
1121                     $callobj = {
1122                         method  => 'setProductActionRequest',
1123                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1124                         id  => 1,
1125                     };
1126                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1127                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1128                     if ($res_err){
1129                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1130                     } else {
1131                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1132                     }
1133                 }
1134             }
1135         }
1136         push(@out_msg_l, &create_xml_string($out_hash));
1137     
1139     # Build wakeup message for client
1140         if (not $error) {
1141             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1142             &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1143             my $wakeup_msg = &create_xml_string($wakeup_hash);
1144             push(@out_msg_l, $wakeup_msg);
1146             # invoke trigger wake for this gosa-si-server
1147             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1148         }
1149     }
1150     
1151     # Return messages
1152         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1153     return @out_msg_l;
1156 ################################
1157 # @brief Set action for an Opsi client
1158 # @param product - STRING - Opsi product
1159 # @param action - STRING - action
1160 # @param hostId - STRING - Opsi hostId
1161 sub _set_action {
1162   my $product= shift;
1163   my $action = shift;
1164   my $hostId = shift;
1165   my $callobj;
1167   $callobj = {
1168     method  => 'setProductActionRequest',
1169     params  => [ $product, $hostId, $action],
1170     id  => 1,
1171   };
1173   $main::opsi_client->call($main::opsi_url, $callobj);
1176 ################################
1177 # @brief Set state for an Opsi client
1178 # @param product - STRING - Opsi product
1179 # @param action - STRING - state
1180 # @param hostId - STRING - Opsi hostId
1181 sub _set_state {
1182   my $product = shift;
1183   my $state = shift;
1184   my $hostId = shift;
1185   my $callobj;
1187   $callobj = {
1188     method  => 'setProductState',
1189     params  => [ $product, $hostId, $state ],
1190     id  => 1,
1191   };
1193   $main::opsi_client->call($main::opsi_url, $callobj);
1196 ################################
1197 # @brief Create a license pool at Opsi server.
1198 # @param licensePoolId The name of the pool (optional). 
1199 # @param description The description of the pool (optional).
1200 # @param productIds A list of assigned porducts of the pool (optional). 
1201 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional). 
1202 sub opsi_createLicensePool {
1203         my $startTime = Time::HiRes::time;
1204     my ($msg, $msg_hash, $session_id) = @_;
1205     my $header = @{$msg_hash->{'header'}}[0];
1206     my $source = @{$msg_hash->{'source'}}[0];
1207     my $target = @{$msg_hash->{'target'}}[0];
1208         my $out_hash;
1209         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1210         my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1211         my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1212         my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1214         # Create license Pool
1215     my $callobj = {
1216         method  => 'createLicensePool',
1217         params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1218         id  => 1,
1219     };
1220     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1222         # Check Opsi error
1223         my ($res_error, $res_error_str) = &check_opsi_res($res);
1224         if ($res_error){
1225                 # Create error message
1226                 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1227                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1228                 return ( &create_xml_string($out_hash) );
1229         }
1231         # Create function result message
1232         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1233         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1235         my $endTime = Time::HiRes::time;
1236         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1237         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1238         return ( &create_xml_string($out_hash) );
1241 ################################
1242 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1243 sub opsi_getLicensePools_listOfHashes {
1244         my $startTime = Time::HiRes::time;
1245     my ($msg, $msg_hash, $session_id) = @_;
1246     my $header = @{$msg_hash->{'header'}}[0];
1247     my $source = @{$msg_hash->{'source'}}[0];
1248         my $out_hash;
1250         # Fetch infos from Opsi server
1251     my $callobj = {
1252         method  => 'getLicensePools_listOfHashes',
1253         params  => [ ],
1254         id  => 1,
1255     };
1256     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1258         # Check Opsi error
1259         my ($res_error, $res_error_str) = &check_opsi_res($res);
1260         if ($res_error){
1261                 # Create error message
1262                 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1263                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1264                 return ( &create_xml_string($out_hash) );
1265         }
1267         # Create function result message
1268         my $res_hash = { 'hit'=> [] };
1269         foreach my $licensePool ( @{$res->result}) {
1270                 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1271                         'description' => [$licensePool->{'description'}],
1272                         'productIds' => $licensePool->{'productIds'},
1273                         'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1274                         };
1275                 push( @{$res_hash->{hit}}, $licensePool_hash );
1276         }
1278         # Create function result message
1279         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1280         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1281         $out_hash->{result} = [$res_hash];
1283         my $endTime = Time::HiRes::time;
1284         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1285         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1286         return ( &create_xml_string($out_hash) );
1289 ################################
1290 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1291 # @param licensePoolId The name of the pool. 
1292 sub opsi_getLicensePool_hash {
1293         my $startTime = Time::HiRes::time;
1294     my ($msg, $msg_hash, $session_id) = @_;
1295     my $header = @{$msg_hash->{'header'}}[0];
1296     my $source = @{$msg_hash->{'source'}}[0];
1297     my $target = @{$msg_hash->{'target'}}[0];
1298     my $licensePoolId;
1299         my $out_hash;
1301         # Check input sanity
1302         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1303                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1304         } else {
1305                 return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
1306         }
1308         # Fetch infos from Opsi server
1309     my $callobj = {
1310         method  => 'getLicensePool_hash',
1311         params  => [ $licensePoolId ],
1312         id  => 1,
1313     };
1314     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1316         # Check Opsi error
1317         my ($res_error, $res_error_str) = &check_opsi_res($res);
1318         if ($res_error){
1319                 # Create error message
1320                 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1321                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1322                 &add_content2xml_hash($out_hash, "error", $res_error_str);
1323                 return ( &create_xml_string($out_hash) );
1324         }
1326         # Create function result message
1327         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1328         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1329         &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1330         &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1331         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1332         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1334         my $endTime = Time::HiRes::time;
1335         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1336         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1337         return ( &create_xml_string($out_hash) );
1340 sub _parse_getSoftwareLicenseUsages {
1341         my $res = shift;
1343         # Parse Opsi result
1344         my $tmp_licensePool_cache = {};
1345         my $res_hash = { 'hit'=> [] };
1346         foreach my $license ( @{$res}) {
1347                 my $tmp_licensePool = $license->{'licensePoolId'};
1348                 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1349                         # Fetch missing informations from Opsi and cache the results for a possible later usage
1350                         my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1351                         if (not $err) {
1352                                 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1353                         }
1354                 }
1355                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1356                         'notes' => [$license->{'notes'}],
1357                         'licenseKey' => [$license->{'licenseKey'}],
1358                         'hostId' => [$license->{'hostId'}],
1359                         'licensePoolId' => [$tmp_licensePool],
1360                         };
1361                 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1362                         $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1363                         map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1364                         map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1365                 }
1366                 push( @{$res_hash->{hit}}, $license_hash );
1367         }
1369         return $res_hash;
1372 ################################
1373 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1374 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1375 # @param licensePoolId The name of the pool (optional). 
1376 sub opsi_getSoftwareLicenseUsages {
1377         my $startTime = Time::HiRes::time;
1378         my ($msg, $msg_hash, $session_id) = @_;
1379         my $header = @{$msg_hash->{'header'}}[0];
1380         my $source = @{$msg_hash->{'source'}}[0];
1381         my $target = @{$msg_hash->{'target'}}[0];
1382         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1383         my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1384         my $out_hash;
1386         my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1387         if ($err){
1388                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1389         }
1391         # Parse Opsi result
1392         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1394         # Create function result message
1395         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1396         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1397         $out_hash->{result} = [$res_hash];
1399         my $endTime = Time::HiRes::time;
1400         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1401         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1402         return ( &create_xml_string($out_hash) );
1405 ################################
1406 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1407 # @param productId Something like 'firefox', 'python' or anything else .
1408 sub opsi_getSoftwareLicenseUsagesForProductId {
1409         my $startTime = Time::HiRes::time;
1410         my ($msg, $msg_hash, $session_id) = @_;
1411         my $header = @{$msg_hash->{'header'}}[0];
1412         my $source = @{$msg_hash->{'source'}}[0];
1414         # Check input sanity
1415         my $productId;
1416         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1417                 $productId= @{$msg_hash->{'productId'}}[0];
1418         } else {
1419                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1420         }
1422         # Fetch licensePoolId for productId
1423         my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1424         if ($err){
1425                 my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
1426                 $out_hash->{result} = [];
1427                 return ( &create_xml_string($out_hash) );
1428         }
1429         my $licensePoolId = $res;   # We assume that there is only one pool for each productID!!!
1431         # Fetch softwareLiceceUsages for licensePoolId
1432         ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1433         if ($err){
1434                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1435         }
1436         # Parse Opsi result
1437         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1439         # Create function result message
1440         my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
1441         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1442         $out_hash->{result} = [$res_hash];
1444         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1445         return ( &create_xml_string($out_hash) );
1448 ################################
1449 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1450 # @param softwareLicenseId Identificator of a license.
1451 sub opsi_getSoftwareLicense_hash {
1452         my $startTime = Time::HiRes::time;
1453         my ($msg, $msg_hash, $session_id) = @_;
1454         my $header = @{$msg_hash->{'header'}}[0];
1455         my $source = @{$msg_hash->{'source'}}[0];
1456         my $target = @{$msg_hash->{'target'}}[0];
1457         my $softwareLicenseId;
1458         my $out_hash;
1460         # Check input sanity
1461         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1462                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1463         } else {
1464                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1465         }
1467         my $callobj = {
1468                 method  => 'getSoftwareLicense_hash',
1469                 params  => [ $softwareLicenseId ],
1470                 id  => 1,
1471         };
1472         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1474         # Check Opsi error
1475         my ($res_error, $res_error_str) = &check_opsi_res($res);
1476         if ($res_error){
1477                 # Create error message
1478                 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1479                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1480                 return ( &create_xml_string($out_hash) );
1481         }
1482         
1483         # Create function result message
1484         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1485         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1486         &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1487         &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1488         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1489         &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1490         foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1491                 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1492                 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1493         }
1495         my $endTime = Time::HiRes::time;
1496         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1497         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1498         return ( &create_xml_string($out_hash) );
1501 ################################
1502 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
1503 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted. 
1504 # @param licensePoolId The name of the pool. 
1505 sub opsi_deleteLicensePool {
1506         my $startTime = Time::HiRes::time;
1507         my ($msg, $msg_hash, $session_id) = @_;
1508     my $header = @{$msg_hash->{'header'}}[0];
1509     my $source = @{$msg_hash->{'source'}}[0];
1510     my $target = @{$msg_hash->{'target'}}[0];
1511     my $licensePoolId;
1512         my $out_hash;
1514         # Check input sanity
1515         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1516                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1517         } else {
1518                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1519         }
1521         # Fetch softwareLicenseIds used in license pool
1522         # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1523         # but not the license contracts of the software licenses. In our case each software license has exactly one license contract. 
1524         my $callobj = {
1525                 method  => 'getSoftwareLicenses_listOfHashes',
1526                 params  => [ ],
1527                 id  => 1,
1528         };
1529         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1531         # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1532         my @lCI_toBeDeleted;
1533         foreach my $softwareLicenseHash ( @{$res->result} ) {
1534                 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) { 
1535                         next; 
1536                 }  
1537                 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1538         }
1540         # Delete license pool at Opsi server
1541     $callobj = {
1542         method  => 'deleteLicensePool',
1543         params  => [ $licensePoolId, 'deleteLicenses=True'  ],
1544         id  => 1,
1545     };
1546     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1547         my ($res_error, $res_error_str) = &check_opsi_res($res);
1548         if ($res_error){
1549                 # Create error message
1550                 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1551                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1552                 return ( &create_xml_string($out_hash) );
1553         } 
1555         # Delete each license contract connected with the license pool
1556         foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1557                 my $callobj = {
1558                         method  => 'deleteLicenseContract',
1559                         params  => [ $licenseContractId ],
1560                         id  => 1,
1561                 };
1562                 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1563                 my ($res_error, $res_error_str) = &check_opsi_res($res);
1564                 if ($res_error){
1565                         # Create error message
1566                         &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1567                         $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1568                         return ( &create_xml_string($out_hash) );
1569                 }
1570         }
1572         # Create function result message
1573         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1574         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1576         my $endTime = Time::HiRes::time;
1577         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1578         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1579         return ( &create_xml_string($out_hash) );
1582 ################################
1583 # @brief Create a license contract, create a software license and add the software license to the license pool
1584 # @param licensePoolId The name of the pool the license should be assigned.
1585 # @param licenseKey The license key.
1586 # @param partner Name of the license partner (optional).
1587 # @param conclusionDate Date of conclusion of license contract (optional)
1588 # @param notificationDate Date of notification that license is running out soon (optional).
1589 # @param notes This is the place for some notes (optional)
1590 # @param softwareLicenseId Identificator of a license (optional).
1591 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1592 # @param maxInstallations The number of clients use this license (optional). 
1593 # @param boundToHost The name of the client the license is bound to (optional).
1594 # @param expirationDate The date when the license is running down (optional). 
1595 sub opsi_createLicense {
1596         my $startTime = Time::HiRes::time;
1597         my ($msg, $msg_hash, $session_id) = @_;
1598     my $header = @{$msg_hash->{'header'}}[0];
1599     my $source = @{$msg_hash->{'source'}}[0];
1600     my $target = @{$msg_hash->{'target'}}[0];
1601         my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1602         my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1603         my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1604         my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1605         my $licenseContractId = undef;
1606         my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1607         my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1608         my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1609         my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1610         my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1611         my $licensePoolId;
1612         my $licenseKey;
1613         my $out_hash;
1615         # Check input sanity
1616         if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1617                 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1618         } else {
1619                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1620         }
1621         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1622                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1623         } else {
1624                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1625         }
1626         if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1627                 return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
1628         }
1629         
1630         # Automatically define licenseContractId if ID is not given
1631         if (defined $softwareLicenseId) { 
1632                 $licenseContractId = "c_".$softwareLicenseId;
1633         }
1635         # Create license contract at Opsi server
1636     my $callobj = {
1637         method  => 'createLicenseContract',
1638         params  => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1639         id  => 1,
1640     };
1641     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1643         # Check Opsi error
1644         my ($res_error, $res_error_str) = &check_opsi_res($res);
1645         if ($res_error){
1646                 # Create error message
1647                 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1648                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1649                 return ( &create_xml_string($out_hash) );
1650         }
1651         
1652         $licenseContractId = $res->result;
1654         # Create software license at Opsi server
1655     $callobj = {
1656         method  => 'createSoftwareLicense',
1657         params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1658         id  => 1,
1659     };
1660     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1662         # Check Opsi error
1663         ($res_error, $res_error_str) = &check_opsi_res($res);
1664         if ($res_error){
1665                 # Create error message
1666                 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1667                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1668                 return ( &create_xml_string($out_hash) );
1669         }
1671         $softwareLicenseId = $res->result;
1673         # Add software license to license pool
1674         $callobj = {
1675         method  => 'addSoftwareLicenseToLicensePool',
1676         params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1677         id  => 1,
1678     };
1679     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1681         # Check Opsi error
1682         ($res_error, $res_error_str) = &check_opsi_res($res);
1683         if ($res_error){
1684                 # Create error message
1685                 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1686                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1687                 return ( &create_xml_string($out_hash) );
1688         }
1690         # Create function result message
1691         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1692         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1693         
1694         my $endTime = Time::HiRes::time;
1695         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1696         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1697         return ( &create_xml_string($out_hash) );
1700 ################################
1701 # @brief Assign a software license to a host
1702 # @param hostid Something like client_1.intranet.mydomain.de
1703 # @param licensePoolId The name of the pool.
1704 sub opsi_assignSoftwareLicenseToHost {
1705         my $startTime = Time::HiRes::time;
1706         my ($msg, $msg_hash, $session_id) = @_;
1707     my $header = @{$msg_hash->{'header'}}[0];
1708     my $source = @{$msg_hash->{'source'}}[0];
1709     my $target = @{$msg_hash->{'target'}}[0];
1710         my $hostId;
1711         my $licensePoolId;
1713         # Check input sanity
1714         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1715                 $hostId = @{$msg_hash->{'hostId'}}[0];
1716         } else {
1717                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1718         }
1719         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1720                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1721         } else {
1722                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1723         }
1725         # Assign a software license to a host
1726         my $callobj = {
1727         method  => 'getAndAssignSoftwareLicenseKey',
1728         params  => [ $hostId, $licensePoolId ],
1729         id  => 1,
1730     };
1731     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1733         # Check Opsi error
1734         my ($res_error, $res_error_str) = &check_opsi_res($res);
1735         if ($res_error){
1736                 # Create error message
1737                 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1738                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1739                 return ( &create_xml_string($out_hash) );
1740         }
1742         # Create function result message
1743         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1744         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1745         
1746         my $endTime = Time::HiRes::time;
1747         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1748         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1749         return ( &create_xml_string($out_hash) );
1752 ################################
1753 # @brief Unassign a software license from a host.
1754 # @param hostid Something like client_1.intranet.mydomain.de
1755 # @param licensePoolId The name of the pool.
1756 sub opsi_unassignSoftwareLicenseFromHost {
1757         my $startTime = Time::HiRes::time;
1758         my ($msg, $msg_hash, $session_id) = @_;
1759     my $header = @{$msg_hash->{'header'}}[0];
1760     my $source = @{$msg_hash->{'source'}}[0];
1761     my $target = @{$msg_hash->{'target'}}[0];
1762         my $hostId;
1763         my $licensePoolId;
1765         # Check input sanity
1766         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1767                 $hostId = @{$msg_hash->{'hostId'}}[0];
1768         } else {
1769                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1770         }
1771         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1772                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1773         } else {
1774                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1775         }
1777         # Unassign a software license from a host
1778         my $callobj = {
1779         method  => 'deleteSoftwareLicenseUsage',
1780         params  => [ $hostId, '', $licensePoolId ],
1781         id  => 1,
1782     };
1783     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1785         # Check Opsi error
1786         my ($res_error, $res_error_str) = &check_opsi_res($res);
1787         if ($res_error){
1788                 # Create error message
1789                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1790                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1791                 return ( &create_xml_string($out_hash) );
1792         }
1794         # Create function result message
1795         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1796         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1797         
1798         my $endTime = Time::HiRes::time;
1799         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1800         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1801         return ( &create_xml_string($out_hash) );
1804 ################################
1805 # @brief Unassign all software licenses from a host
1806 # @param hostid Something like client_1.intranet.mydomain.de
1807 sub opsi_unassignAllSoftwareLicensesFromHost {
1808         my $startTime = Time::HiRes::time;
1809         my ($msg, $msg_hash, $session_id) = @_;
1810     my $header = @{$msg_hash->{'header'}}[0];
1811     my $source = @{$msg_hash->{'source'}}[0];
1812     my $target = @{$msg_hash->{'target'}}[0];
1813         my $hostId;
1815         # Check input sanity
1816         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1817                 $hostId = @{$msg_hash->{'hostId'}}[0];
1818         } else {
1819                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1820         }
1822         # Unassign all software licenses from a host
1823         my $callobj = {
1824         method  => 'deleteAllSoftwareLicenseUsages',
1825         params  => [ $hostId ],
1826         id  => 1,
1827     };
1828     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1830         # Check Opsi error
1831         my ($res_error, $res_error_str) = &check_opsi_res($res);
1832         if ($res_error){
1833                 # Create error message
1834                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1835                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1836                 return ( &create_xml_string($out_hash) );
1837         }
1839         # Create function result message
1840         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1841         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1842         
1843         my $endTime = Time::HiRes::time;
1844         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1845         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1846         return ( &create_xml_string($out_hash) );
1850 ################################
1851 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1852 # and the number of max and remaining installations for a given OPSI product.
1853 # @param productId Identificator of an OPSI product.
1854 sub opsi_getLicenseInformationForProduct {
1855         my $startTime = Time::HiRes::time;
1856     my ($msg, $msg_hash, $session_id) = @_;
1857     my $header = @{$msg_hash->{'header'}}[0];
1858     my $source = @{$msg_hash->{'source'}}[0];
1859         my $productId;
1860         my $out_hash;
1862         # Check input sanity
1863         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1864                 $productId = @{$msg_hash->{'productId'}}[0];
1865         } else {
1866                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1867         }
1869         # Fetch infos from Opsi server
1870     my $callobj = {
1871         method  => 'getLicensePoolId',
1872         params  => [ $productId ],
1873         id  => 1,
1874     };
1875     #my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1876     my $res = $opsi_client->call($opsi_url, $callobj);
1878         # Check Opsi error
1879         my ($res_error, $res_error_str) = &check_opsi_res($res);
1880         if ($res_error){
1881                 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
1882         } 
1883         
1884         my $licensePoolId = $res->result;
1886         # Fetch statistic information for given pool ID
1887         $callobj = {
1888                 method  => 'getLicenseStatistics_hash',
1889                 params  => [ ],
1890                 id  => 1,
1891         };
1892         $res = $opsi_client->call($opsi_url, $callobj);
1894         # Check Opsi error
1895         ($res_error, $res_error_str) = &check_opsi_res($res);
1896         if ($res_error){
1897                 # Create error message
1898                 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
1899                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1900                 return ( &create_xml_string($out_hash) );
1901         }
1903         # Create function result message
1904         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1905         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1906         &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1907         &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
1908         &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
1909         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
1910         &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
1911         map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
1913         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1914         return ( &create_xml_string($out_hash) );
1918 ################################
1919 # @brief Returns licensePoolId, description, a list of productIds, al list of windowsSoftwareIds and a list of licenses for a given licensePoolId. 
1920 # Each license contains softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseKeys, hostIds, expirationDate, boundToHost and licenseContractId.
1921 # The licenseContract contains conclusionDate, expirationDate, notes, notificationDate and partner. 
1922 # @param licensePoolId The name of the pool.
1923 sub opsi_getPool {
1924         my $startTime = Time::HiRes::time;
1925     my ($msg, $msg_hash, $session_id) = @_;
1926     my $header = @{$msg_hash->{'header'}}[0];
1927     my $source = @{$msg_hash->{'source'}}[0];
1929         # Check input sanity
1930         my $licensePoolId;
1931         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1932                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1933         } else {
1934                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1935         }
1937         # Create hash for the answer
1938         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1939         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1941         # Call Opsi
1942         my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
1943         if ($err){
1944                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
1945         }
1946         # Add data to outgoing hash
1947         &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
1948         &add_content2xml_hash($out_hash, "description", $res->{'description'});
1949         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
1950         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
1953         # Call Opsi two times
1954         my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1955         if ($usages_err){
1956                 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
1957         }
1958         my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
1959         if ($licenses_err){
1960                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
1961         }
1963         # Add data to outgoing hash
1964         # Parse through all software licenses and select those associated to the pool
1965         my $res_hash = { 'hit'=> [] };
1966         foreach my $license ( @$licenses_res) {
1967                 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
1968                 my $found = 0;
1969                 my @licensePoolIds_list = @{$license->{licensePoolIds}};
1970                 foreach my $lPI ( @licensePoolIds_list) {
1971                         if ($lPI eq $licensePoolId) { $found++ }
1972                 }
1973                 if (not $found ) { next; };
1974                 # Found matching licensePoolId
1975                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1976                         'licenseKeys' => {},
1977                         'expirationDate' => [$license->{'expirationDate'}],
1978                         'boundToHost' => [$license->{'boundToHost'}],
1979                         'maxInstallations' => [$license->{'maxInstallations'}],
1980                         'licenseType' => [$license->{'licenseType'}],
1981                         'licenseContractId' => [$license->{'licenseContractId'}],
1982                         'licensePoolIds' => [],
1983                         'hostIds' => [],
1984                         };
1985                 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
1986                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
1987                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
1988                 }
1989                 foreach my $usage (@$usages_res) {
1990                         # Search for hostIds with matching softwareLicenseId
1991                         if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
1992                                 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
1993                         }
1994                 }
1996                 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
1997                 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
1998                 if ($lContract_err){
1999                         return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
2000                 }
2001                 $license_hash->{$license->{'licenseContractId'}} = [];
2002                 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
2003                         'notificationDate' => [$lContract_res->{notificationDate}],
2004                         'notes' => [$lContract_res->{notes}],
2005                         'exirationDate' => [$lContract_res->{expirationDate}],
2006                         'partner' => [$lContract_res->{partner}],
2007                 };
2008                 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2010                 push( @{$res_hash->{hit}}, $license_hash );
2011         }
2012         $out_hash->{licenses} = [$res_hash];
2014         my $endTime = Time::HiRes::time;
2015         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2016         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2017     return ( &create_xml_string($out_hash) );
2021 ################################
2022 # @brief Removes at first the software license from license pool and than deletes the software license. 
2023 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2024 # @param softwareLicenseId Identificator of a license.
2025 # @param licensePoolId The name of the pool.
2026 sub opsi_removeLicense {
2027         my $startTime = Time::HiRes::time;
2028     my ($msg, $msg_hash, $session_id) = @_;
2029     my $header = @{$msg_hash->{'header'}}[0];
2030     my $source = @{$msg_hash->{'source'}}[0];
2032         # Check input sanity
2033         my $softwareLicenseId;
2034         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2035                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2036         } else {
2037                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2038         }
2039         my $licensePoolId;
2040         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2041                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2042         } else {
2043                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2044         }
2045         
2046         # Call Opsi
2047         my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2048         if ($err){
2049                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2050         }
2052         # Call Opsi
2053         ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2054         if ($err){
2055                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2056         }
2058         # Create hash for the answer
2059         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2060         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2061         my $endTime = Time::HiRes::time;
2062         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2063         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2064         return ( &create_xml_string($out_hash) );
2068 ################################
2069 # @brief Return softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseContractId, expirationDate, boundToHost and a list of productIds.
2070 # @param hostId Something like client_1.intranet.mydomain.de
2071 sub opsi_getReservedLicenses {
2072         my $startTime = Time::HiRes::time;
2073         my ($msg, $msg_hash, $session_id) = @_;
2074         my $header = @{$msg_hash->{'header'}}[0];
2075         my $source = @{$msg_hash->{'source'}}[0];
2077         # Check input sanity
2078         my $hostId;
2079         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2080                 $hostId = @{$msg_hash->{'hostId'}}[0];
2081         } else {
2082                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2083         }
2085         # Fetch informations from Opsi server
2086         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2087         if ($license_err){
2088                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2089         }
2091         # Parse result
2092         my $res_hash = { 'hit'=> [] };
2093         foreach my $license ( @$license_res) {
2094                 if ($license->{boundToHost} ne $hostId) { next; }
2096                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2097                         'maxInstallations' => [$license->{'maxInstallations'}],
2098                         'boundToHost' => [$license->{'boundToHost'}],
2099                         'expirationDate' => [$license->{'expirationDate'}],
2100                         'licenseContractId' => [$license->{'licenseContractId'}],
2101                         'licenseType' => [$license->{'licenseType'}],
2102                         'licensePoolIds' => [],
2103                         };
2104                 
2105                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2106                         # Fetch information for license pools containing a software license which is bound to given host
2107                         my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2108                         if ($pool_err){
2109                                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2110                         }
2112                         # Add licensePool information to result hash
2113                         push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2114                         $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2115                         map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2116                         map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2117                 }
2118                 push( @{$res_hash->{hit}}, $license_hash );
2119         }
2120         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2121         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2122         $out_hash->{licenses} = [$res_hash];
2124         my $endTime = Time::HiRes::time;
2125         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2126         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2127     return ( &create_xml_string($out_hash) );
2130 ################################
2131 # @brief Bound the given softwareLicenseId to the given host.
2132 # @param hostId Opsi hostId
2133 # @param softwareLicenseId Identificator of a license (optional).
2134 sub opsi_boundHostToLicense {
2135         my $startTime = Time::HiRes::time;
2136         my ($msg, $msg_hash, $session_id) = @_;
2137         my $header = @{$msg_hash->{'header'}}[0];
2138         my $source = @{$msg_hash->{'source'}}[0];
2140         # Check input sanity
2141         my $hostId;
2142         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2143                 $hostId = @{$msg_hash->{'hostId'}}[0];
2144         } else {
2145                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2146         }
2147         my $softwareLicenseId;
2148         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2149                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2150         } else {
2151                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2152         }
2154         # Fetch informations from Opsi server
2155         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2156         if ($license_err){
2157                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2158         }
2160         # Memorize parameter for given softwareLicenseId
2161         my $licenseContractId;
2162         my $licenseType;
2163         my $maxInstallations;
2164         my $boundToHost;
2165         my $expirationDate = "";
2166         my $found;
2167         foreach my $license (@$license_res) {
2168                 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2169                 $licenseContractId = $license->{licenseContractId};
2170                 $licenseType = $license->{licenseType};
2171                 $maxInstallations = $license->{maxInstallations};
2172                 $expirationDate = $license->{expirationDate};
2173                 $found++;
2174         }
2176         if (not $found) {
2177                 return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
2178         }
2180         # Set boundToHost option for a given software license
2181         my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId, 
2182                         'licenseContractId' => $licenseContractId, 
2183                         'licenseType' => $licenseType, 
2184                         'maxInstallations' => $maxInstallations, 
2185                         'boundToHost' => $hostId, 
2186                         'expirationDate' => $expirationDate);
2187         if ($bound_err) {
2188                 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2189         }
2191         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2192         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2194         my $endTime = Time::HiRes::time;
2195         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2196         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2197     return ( &create_xml_string($out_hash) );
2200 ################################
2201 # @brief Release a software license formerly bound to a host.
2202 # @param softwareLicenseId Identificator of a license.
2203 sub opsi_unboundHostFromLicense {
2204         # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2205         # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2206         # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2207         my $startTime = Time::HiRes::time;
2208         my ($msg, $msg_hash, $session_id) = @_;
2209         my $header = @{$msg_hash->{'header'}}[0];
2210         my $source = @{$msg_hash->{'source'}}[0];
2212         # Check input sanity
2213         my $softwareLicenseId;
2214         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2215                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2216         } else {
2217                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2218         }
2219         
2220         # Memorize parameter witch are required for this procedure
2221         my $licenseContractId;
2222         my $licenseType;
2223         my $maxInstallations;
2224         my $expirationDate;
2225         my $partner;
2226         my $conclusionDate;
2227         my $notificationDate;
2228         my $notes;
2229         my $licensePoolId;
2230         my $licenseKey;
2232         # Fetch license informations from Opsi server
2233         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2234         if ($license_err){
2235                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2236         }
2237         my $found = 0;
2238         foreach my $license (@$license_res) {
2239                 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2240                 $licenseContractId = $license->{licenseContractId};
2241                 $licenseType = $license->{licenseType};
2242                 $maxInstallations = $license->{maxInstallations};
2243                 $expirationDate = $license->{expirationDate};
2244                 $licensePoolId = @{$license->{licensePoolIds}}[0];
2245                 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2246                 $found++;
2247         }
2248         
2249         # Fetch contract informations from Opsi server
2250         my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2251         if ($contract_err){
2252                 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2253         }
2254         $partner = $contract_res->{partner};
2255         $conclusionDate = $contract_res->{conclusionDate};
2256         $notificationDate = $contract_res->{notificationDate};
2257         $expirationDate = $contract_res->{expirationDate};
2258         $notes = $contract_res->{notes};
2260         # Delete software license
2261         my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2262         if ($err) {
2263                 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2264         }
2266         # Recreate software license without boundToHost
2267         ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate, 
2268                         'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2269         if ($err) {
2270                 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2271         }
2272         ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType, 
2273                         'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate       );
2274         if ($err) {
2275                 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2276         }
2277         ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2278         if ($err) {
2279                 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2280         }
2282         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2283         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2285         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2286     return ( &create_xml_string($out_hash) );
2289 ################################
2290 # @brief Returns a list of licenses with softwaerLicenseId, maxInstallations, boundToHost, expirationDate, licenseContractId, licenseType, a list of licensePoolIds with associated licenseKeys
2291 sub opsi_getAllSoftwareLicenses {
2292         my $startTime = Time::HiRes::time;
2293         my ($msg, $msg_hash, $session_id) = @_;
2294         my $header = @{$msg_hash->{'header'}}[0];
2295         my $source = @{$msg_hash->{'source'}}[0];
2297         my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
2298         if ($err) {
2299                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
2300         }
2302         # Parse result
2303         my $res_hash = { 'hit'=> [] };
2304         foreach my $license ( @$res) {
2305                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2306                         'maxInstallations' => [$license->{'maxInstallations'}],
2307                         'boundToHost' => [$license->{'boundToHost'}],
2308                         'expirationDate' => [$license->{'expirationDate'}],
2309                         'licenseContractId' => [$license->{'licenseContractId'}],
2310                         'licenseType' => [$license->{'licenseType'}],
2311                         'licensePoolIds' => [],
2312                         'licenseKeys'=> {}
2313                         };
2314                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2315                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2316                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2317                 }
2318                 push( @{$res_hash->{hit}}, $license_hash );
2319         }
2320         
2321         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2322         $out_hash->{licenses} = [$res_hash];
2323         if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2325         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2326     return ( &create_xml_string($out_hash) );
2330 ################################
2331 # @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 
2332 # @param hostId Opsi hostId
2333 sub opsi_get_full_product_host_information {
2334         my $startTime = Time::HiRes::time;
2335         my ($msg, $msg_hash, $session_id) = @_;
2336         my $header = @{$msg_hash->{'header'}}[0];
2337         my $source = @{$msg_hash->{'source'}}[0];
2338         my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
2339         my $hostId;
2341         my ($res, $err) = &_get_full_product_host_information( hostId=>@{$msg_hash->{'hostId'}}[0]);
2342         if ($err) {
2343                 return &_giveErrorFeedback($msg_hash, "cannot fetch full_product_host_information from Opsi server : ".$res, $session_id);
2344         }
2346         # Build return message with twisted target and source
2347         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2348         if (defined $forward_to_gosa) {
2349             &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
2350         }
2351         &add_content2xml_hash($out_hash, "xxx", "");
2353         # Get hostId if defined
2354         if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
2355             $hostId = @{$msg_hash->{'hostId'}}[0];
2356             &add_content2xml_hash($out_hash, "hostId", $hostId);
2357         }
2359         # Move to XML string
2360         my $xml_msg= &create_xml_string($out_hash);
2361         
2362         # Convert result in something usable
2363         my $replace= "";
2364         foreach my $product ( @$res) {
2366           # Open item
2367           $replace.= "<item>";
2369           # Add flat hash information
2370           my @entries= ( "priority", "onceScript", "licenseRequired", "packageVersion", "productVersion", "advice",
2371                               "setupScript", "windowsSoftwareIds", "installationStatus", "pxeConfigTemplate", "name", "type",
2372                               "creationTimestamp", "alwaysScript", "productId", "description", "actionRequest", "uninstallScript",
2373                               "action", "updateScript", "productClassNames");
2374           foreach my $entry (@entries) {
2375             if (defined $product->{$entry}) {
2376               my $value= $product->{$entry};
2378               if(ref($value) eq 'ARRAY'){
2379                 my $tmp= "";
2380                 foreach my $element (@$value) {
2381                   $tmp.= "<element>$element</element>";
2382                 }
2383                 $replace.= "<$entry>$tmp</$entry>";
2384               } else {
2385                 $replace.= "<$entry>$value</$entry>";
2386               }
2387             }
2388           }
2390           # Add property information
2391           if (defined $product->{'properties'}) {
2392             $replace.= "<properties>";
2393             while ((my $key, my $value) = each(%{$product->{'properties'}})){
2394               $replace.= "<$key>";
2396               while ((my $pkey, my $pvalue) = each(%$value)){
2397                 if(ref($pvalue) eq 'ARRAY'){
2398                   my $tmp= "";
2399                   foreach my $element (@$pvalue) {
2400                     $tmp.= "<element>$element</element>";
2401                   }
2402                   $replace.= "<$pkey>$tmp</$pkey>";
2403                 } else {
2404                   $replace.= "<$pkey>$pvalue</$pkey>";
2405                 }
2406               }
2407               $replace.= "</$key>";
2408             }
2409             $replace.= "</properties>";
2410           }
2412           # Close item
2413           $replace.= "</item>";
2414         }
2416         $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
2418         &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2419     return ( $xml_msg );
2423 sub opsi_test {
2424     my ($msg, $msg_hash, $session_id) = @_;
2425     my $header = @{$msg_hash->{'header'}}[0];
2426     my $source = @{$msg_hash->{'source'}}[0];
2427         my $pram1 = @{$msg_hash->{'productId'}}[0];
2430         # Fetch infos from Opsi server
2431     my $callobj = {
2432         method  => 'getLicensePoolId',
2433         params  => [ $pram1 ],
2434         id  => 1,
2435     };
2436     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2438         return ();
2442 # ----------------------------------------------------------------------------
2443 #  internal methods handling the comunication with Opsi
2444 # ----------------------------------------------------------------------------
2446 ################################
2447 # @brief Checks if there is a specified tag and if the the tag has a content.
2448 sub _check_xml_tag_is_ok {
2449         my ($msg_hash,$tag) = @_;
2450         if (not defined $msg_hash->{$tag}) {
2451                 $_ = "message contains no tag '$tag'";
2452                 return 0;
2453         }
2454         if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
2455                 $_ = "message tag '$tag' has no content";
2456                 return  0;
2457         }
2458         return 1;
2461 ################################
2462 # @brief Writes the log line and returns the error message for GOsa.
2463 sub _giveErrorFeedback {
2464         my ($msg_hash, $err_string, $session_id) = @_;
2465         &main::daemon_log("$session_id ERROR: $err_string", 1);
2466         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2467     if (exists $msg_hash->{forward_to_gosa}) {
2468         &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
2469     }
2470         return ( &create_xml_string($out_hash) );
2474 ################################
2475 # @brief Perform the call to the Opsi server and measure the time for the call
2476 sub _callOpsi {
2477         my %arg = ('method'=>undef, 'params'=>[], 'id'=>1, @_);
2479         my $callObject = {
2480                 method => $arg{method},
2481                 params => $arg{params},
2482                 id => $arg{id},
2483         };
2485         my $startTime = Time::HiRes::time;
2486         my $opsiResult = $opsi_client->call($opsi_url, $callObject);
2487         my $endTime = Time::HiRes::time;
2488         my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2490         &main::daemon_log("0 DEBUG: time to process opsi call '$arg{method}' : $elapsedTime seconds", 1034); 
2492         return $opsiResult;
2495 sub _getLicensePool_hash {
2496         my %arg = ( 'licensePoolId' => undef, @_ );
2498         if (not defined $arg{licensePoolId} ) { 
2499                 return ("function requires licensePoolId as parameter", 1);
2500         }
2502         my $res = &_callOpsi( method  => 'getLicensePool_hash', params =>[$arg{licensePoolId}], id  => 1 );
2503         my ($res_error, $res_error_str) = &check_opsi_res($res);
2504         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2506         return ($res->result, 0);
2509 sub _getSoftwareLicenses_listOfHashes {
2510         
2511         my $res = &_callOpsi( method  => 'getSoftwareLicenses_listOfHashes' );
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 _getSoftwareLicenseUsages_listOfHashes {
2519         my %arg = ( 'hostId' => "", 'licensePoolId' => "", @_ );
2521         my $res = &_callOpsi( method=>'getSoftwareLicenseUsages_listOfHashes', params=>[ $arg{hostId}, $arg{licensePoolId} ] );
2522         my ($res_error, $res_error_str) = &check_opsi_res($res);
2523         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2525         return ($res->result, 0);
2528 sub _removeSoftwareLicenseFromLicensePool {
2529         my %arg = ( 'softwareLicenseId' => undef, 'licensePoolId' => undef, @_ );
2531         if (not defined $arg{softwareLicenseId} ) { 
2532                 return ("function requires softwareLicenseId as parameter", 1);
2533                 }
2534                 if (not defined $arg{licensePoolId} ) { 
2535                 return ("function requires licensePoolId as parameter", 1);
2536         }
2538         my $res = &_callOpsi( method=>'removeSoftwareLicenseFromLicensePool', params=>[ $arg{softwareLicenseId}, $arg{licensePoolId} ] );
2539         my ($res_error, $res_error_str) = &check_opsi_res($res);
2540         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2542         return ($res->result, 0);
2545 sub _deleteSoftwareLicense {
2546         my %arg = ( 'softwareLicenseId' => undef, 'removeFromPools' => "false", @_ );
2548         if (not defined $arg{softwareLicenseId} ) { 
2549                 return ("function requires softwareLicenseId as parameter", 1);
2550         }
2551         my $removeFromPools = "";
2552         if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) { 
2553                 $removeFromPools = "removeFromPools";
2554         }
2556         my $res = &_callOpsi( method=>'deleteSoftwareLicense', params=>[ $arg{softwareLicenseId}, $removeFromPools ] );
2557         my ($res_error, $res_error_str) = &check_opsi_res($res);
2558         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2560         return ($res->result, 0);
2563 sub _getLicensePoolId {
2564         my %arg = ( 'productId' => undef, @_ );
2565         
2566         if (not defined $arg{productId} ) {
2567                 return ("function requires productId as parameter", 1);
2568         }
2570     my $res = &_callOpsi( method  => 'getLicensePoolId', params  => [ $arg{productId} ] );
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 _getLicenseContract_hash {
2578         my %arg = ( 'licenseContractId' => undef, @_ );
2579         
2580         if (not defined $arg{licenseContractId} ) {
2581                 return ("function requires licenseContractId as parameter", 1);
2582         }
2584     my $res = &_callOpsi( method  => 'getLicenseContract_hash', params  => [ $arg{licenseContractId} ] );
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 _createLicenseContract {
2592         my %arg = (
2593                         'licenseContractId' => undef,
2594                         'partner' => undef,
2595                         'conclusionDate' => undef,
2596                         'notificationDate' => undef,
2597                         'expirationDate' => undef,
2598                         'notes' => undef,
2599                         @_ );
2601         my $res = &_callOpsi( method  => 'createLicenseContract', 
2602                         params  => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2603                         );
2604         my ($res_error, $res_error_str) = &check_opsi_res($res);
2605         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2607         return ($res->result, 0);
2610 sub _createSoftwareLicense {
2611         my %arg = (
2612                         'softwareLicenseId' => undef,
2613                         'licenseContractId' => undef,
2614                         'licenseType' => undef,
2615                         'maxInstallations' => undef,
2616                         'boundToHost' => undef,
2617                         'expirationDate' => undef,
2618                         @_ );
2620     my $res = &_callOpsi( method  => 'createSoftwareLicense',
2621         params  => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2622                 );
2623         my ($res_error, $res_error_str) = &check_opsi_res($res);
2624         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2626         return ($res->result, 0);
2629 sub _addSoftwareLicenseToLicensePool {
2630         my %arg = (
2631             'softwareLicenseId' => undef,
2632             'licensePoolId' => undef,
2633             'licenseKey' => undef,
2634             @_ );
2636         if (not defined $arg{softwareLicenseId} ) {
2637                 return ("function requires softwareLicenseId as parameter", 1);
2638         }
2639         if (not defined $arg{licensePoolId} ) {
2640                 return ("function requires licensePoolId as parameter", 1);
2641         }
2643         my $res = &_callOpsi( method  => 'addSoftwareLicenseToLicensePool', params  => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ] );
2644         my ($res_error, $res_error_str) = &check_opsi_res($res);
2645         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2647         return ($res->result, 0);
2650 sub _getProductStates_hash {
2651         my %arg = (     'hostId' => undef, @_ );
2653         if (not defined $arg{hostId} ) {
2654                 return ("function requires hostId as parameter", 1);
2655         }
2657         my $res = &_callOpsi( method => 'getProductStates_hash', params => [$arg{hostId}]);
2658         my ($res_error, $res_error_str) = &check_opsi_res($res);
2659         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2661         return ($res->result, 0);
2664 sub _get_full_product_host_information {
2665         my %arg = ( 'hostId' => undef, @_ );
2667         if (not defined $arg{hostId}) {
2668                 return ("function requires hostId as parameter", 1);
2669         }
2671         my $res = &_callOpsi( method => 'getFullProductHostInformation_list',  params => [$arg{hostId}]);
2672         my ($res_error, $res_error_str) = &check_opsi_res($res);
2673         if ($res_error){ return ((caller(0))[3]." : ".$res_error_str, 1); }
2675         return ($res->result, 0);
2678 1;