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