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