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_removeLicense",
38 "opsi_getReservedLicenses",
39 "opsi_boundHostToLicense",
40 "opsi_unboundHostFromLicense",
41 "opsi_test",
42 );
43 @EXPORT = @events;
45 use strict;
46 use warnings;
47 use GOSA::GosaSupportDaemon;
48 use Data::Dumper;
49 use XML::Quote qw(:all);
51 BEGIN {}
53 END {}
55 # ----------------------------------------------------------------------------
56 # D E C L A R A T I O N S
57 # ----------------------------------------------------------------------------
59 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
63 # ----------------------------------------------------------------------------
64 # S U B R O U T I N E S
65 # ----------------------------------------------------------------------------
68 ################################
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 #
73 sub get_events {
74 return \@events;
75 }
77 ################################
78 #
79 # @brief Checks if there is a specified tag and if the the tag has a content.
80 # @return 0|1
81 #
82 sub _check_xml_tag_is_ok {
83 my ($msg_hash,$tag) = @_;
84 if (not defined $msg_hash->{$tag}) {
85 $_ = "message contains no tag '$tag'";
86 return 0;
87 }
88 if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
89 $_ = "message tag '$tag' has no content";
90 return 0;
91 }
92 return 1;
93 }
95 ################################
96 #
97 # @brief Writes the log line and returns the error message for GOsa.
98 #
99 sub _give_feedback {
100 my ($msg, $msg_hash, $session_id, $error) = @_;
101 &main::daemon_log("$session_id ERROR: $error: ".$msg, 1);
102 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{'source'}}[0], $error);
103 return &create_xml_string($out_hash);
104 }
106 ## @method opsi_add_product_to_client
107 # Adds an Opsi product to an Opsi client.
108 # @param msg - STRING - xml message with tags hostId and productId
109 # @param msg_hash - HASHREF - message information parsed into a hash
110 # @param session_id - INTEGER - POE session id of the processing of this message
111 # @return out_msg - STRING - feedback to GOsa in success and error case
112 sub opsi_add_product_to_client {
113 my ($msg, $msg_hash, $session_id) = @_;
114 my $header = @{$msg_hash->{'header'}}[0];
115 my $source = @{$msg_hash->{'source'}}[0];
116 my $target = @{$msg_hash->{'target'}}[0];
117 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
118 my ($hostId, $productId);
119 my $error = 0;
121 # Build return message
122 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
123 if (defined $forward_to_gosa) {
124 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
125 }
127 # Sanity check of needed parameter
128 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
129 $error++;
130 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
131 &add_content2xml_hash($out_hash, "error", "hostId");
132 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
134 }
135 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
136 $error++;
137 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
138 &add_content2xml_hash($out_hash, "error", "productId");
139 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
140 }
142 if (not $error) {
143 # Get hostId
144 $hostId = @{$msg_hash->{'hostId'}}[0];
145 &add_content2xml_hash($out_hash, "hostId", $hostId);
147 # Get productID
148 $productId = @{$msg_hash->{'productId'}}[0];
149 &add_content2xml_hash($out_hash, "productId", $productId);
151 # Do an action request for all these -> "setup".
152 my $callobj = {
153 method => 'setProductActionRequest',
154 params => [ $productId, $hostId, "setup" ],
155 id => 1, };
157 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
158 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
159 if ($sres_err){
160 &main::daemon_log("$session_id ERROR: cannot add product: ".$sres_err_string, 1);
161 &add_content2xml_hash($out_hash, "error", $sres_err_string);
162 }
163 }
165 # return message
166 return ( &create_xml_string($out_hash) );
167 }
169 ## @method opsi_del_product_from_client
170 # Deletes an Opsi-product from an Opsi-client.
171 # @param msg - STRING - xml message with tags hostId and productId
172 # @param msg_hash - HASHREF - message information parsed into a hash
173 # @param session_id - INTEGER - POE session id of the processing of this message
174 # @return out_msg - STRING - feedback to GOsa in success and error case
175 sub opsi_del_product_from_client {
176 my ($msg, $msg_hash, $session_id) = @_;
177 my $header = @{$msg_hash->{'header'}}[0];
178 my $source = @{$msg_hash->{'source'}}[0];
179 my $target = @{$msg_hash->{'target'}}[0];
180 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
181 my ($hostId, $productId);
182 my $error = 0;
183 my ($sres, $sres_err, $sres_err_string);
185 # Build return message
186 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
187 if (defined $forward_to_gosa) {
188 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
189 }
191 # Sanity check of needed parameter
192 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
193 $error++;
194 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
195 &add_content2xml_hash($out_hash, "error", "hostId");
196 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
198 }
199 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
200 $error++;
201 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
202 &add_content2xml_hash($out_hash, "error", "productId");
203 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
204 }
206 # All parameter available
207 if (not $error) {
208 # Get hostId
209 $hostId = @{$msg_hash->{'hostId'}}[0];
210 &add_content2xml_hash($out_hash, "hostId", $hostId);
212 # Get productID
213 $productId = @{$msg_hash->{'productId'}}[0];
214 &add_content2xml_hash($out_hash, "productId", $productId);
217 # : check the results for more than one entry which is currently installed
218 #$callobj = {
219 # method => 'getProductDependencies_listOfHashes',
220 # params => [ $productId ],
221 # id => 1, };
222 #
223 #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
224 #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
225 #if ($sres_err){
226 # &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
227 # &add_content2xml_hash($out_hash, "error", $sres_err_string);
228 # return ( &create_xml_string($out_hash) );
229 #}
232 # Check to get product action list
233 my $callobj = {
234 method => 'getPossibleProductActions_list',
235 params => [ $productId ],
236 id => 1, };
237 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
238 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
239 if ($sres_err){
240 &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
241 &add_content2xml_hash($out_hash, "error", $sres_err_string);
242 $error++;
243 }
244 }
246 # Check action uninstall of product
247 if (not $error) {
248 my $uninst_possible= 0;
249 foreach my $r (@{$sres->result}) {
250 if ($r eq 'uninstall') {
251 $uninst_possible= 1;
252 }
253 }
254 if (!$uninst_possible){
255 &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
256 &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
257 $error++;
258 }
259 }
261 # Set product state to "none"
262 # Do an action request for all these -> "setup".
263 if (not $error) {
264 my $callobj = {
265 method => 'setProductActionRequest',
266 params => [ $productId, $hostId, "none" ],
267 id => 1,
268 };
269 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
270 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
271 if ($sres_err){
272 &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
273 &add_content2xml_hash($out_hash, "error", $sres_err_string);
274 }
275 }
277 # Return message
278 return ( &create_xml_string($out_hash) );
279 }
281 ## @method opsi_add_client
282 # Adds an Opsi client to Opsi.
283 # @param msg - STRING - xml message with tags hostId and macaddress
284 # @param msg_hash - HASHREF - message information parsed into a hash
285 # @param session_id - INTEGER - POE session id of the processing of this message
286 # @return out_msg - STRING - feedback to GOsa in success and error case
287 sub opsi_add_client {
288 my ($msg, $msg_hash, $session_id) = @_;
289 my $header = @{$msg_hash->{'header'}}[0];
290 my $source = @{$msg_hash->{'source'}}[0];
291 my $target = @{$msg_hash->{'target'}}[0];
292 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
293 my ($hostId, $mac);
294 my $error = 0;
295 my ($sres, $sres_err, $sres_err_string);
297 # Build return message with twisted target and source
298 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
299 if (defined $forward_to_gosa) {
300 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
301 }
303 # Sanity check of needed parameter
304 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
305 $error++;
306 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
307 &add_content2xml_hash($out_hash, "error", "hostId");
308 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
309 }
310 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH')) {
311 $error++;
312 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
313 &add_content2xml_hash($out_hash, "error", "macaddress");
314 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
315 }
317 if (not $error) {
318 # Get hostId
319 $hostId = @{$msg_hash->{'hostId'}}[0];
320 &add_content2xml_hash($out_hash, "hostId", $hostId);
322 # Get macaddress
323 $mac = @{$msg_hash->{'macaddress'}}[0];
324 &add_content2xml_hash($out_hash, "macaddress", $mac);
326 my $name= $hostId;
327 $name=~ s/^([^.]+).*$/$1/;
328 my $domain= $hostId;
329 $domain=~ s/^[^.]+\.(.*)$/$1/;
330 my ($description, $notes, $ip);
332 if (defined @{$msg_hash->{'description'}}[0]){
333 $description = @{$msg_hash->{'description'}}[0];
334 }
335 if (defined @{$msg_hash->{'notes'}}[0]){
336 $notes = @{$msg_hash->{'notes'}}[0];
337 }
338 if (defined @{$msg_hash->{'ip'}}[0]){
339 $ip = @{$msg_hash->{'ip'}}[0];
340 }
342 my $callobj;
343 $callobj = {
344 method => 'createClient',
345 params => [ $name, $domain, $description, $notes, $ip, $mac ],
346 id => 1,
347 };
349 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
350 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
351 if ($sres_err){
352 &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
353 &add_content2xml_hash($out_hash, "error", $sres_err_string);
354 } else {
355 &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5);
356 }
357 }
359 # Return message
360 return ( &create_xml_string($out_hash) );
361 }
363 ## @method opsi_modify_client
364 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
365 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
366 # @param msg_hash - HASHREF - message information parsed into a hash
367 # @param session_id - INTEGER - POE session id of the processing of this message
368 # @return out_msg - STRING - feedback to GOsa in success and error case
369 sub opsi_modify_client {
370 my ($msg, $msg_hash, $session_id) = @_;
371 my $header = @{$msg_hash->{'header'}}[0];
372 my $source = @{$msg_hash->{'source'}}[0];
373 my $target = @{$msg_hash->{'target'}}[0];
374 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
375 my $hostId;
376 my $error = 0;
377 my ($sres, $sres_err, $sres_err_string);
379 # Build return message with twisted target and source
380 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
381 if (defined $forward_to_gosa) {
382 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
383 }
385 # Sanity check of needed parameter
386 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
387 $error++;
388 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
389 &add_content2xml_hash($out_hash, "error", "hostId");
390 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
391 }
393 if (not $error) {
394 # Get hostId
395 $hostId = @{$msg_hash->{'hostId'}}[0];
396 &add_content2xml_hash($out_hash, "hostId", $hostId);
397 my $name= $hostId;
398 $name=~ s/^([^.]+).*$/$1/;
399 my $domain= $hostId;
400 $domain=~ s/^[^.]+(.*)$/$1/;
402 # Modify description, notes or mac if defined
403 my ($description, $notes, $mac);
404 my $callobj;
405 if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
406 $description = @{$msg_hash->{'description'}}[0];
407 $callobj = {
408 method => 'setHostDescription',
409 params => [ $hostId, $description ],
410 id => 1,
411 };
412 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
413 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
414 if ($sres_err){
415 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
416 &add_content2xml_hash($out_hash, "error", $sres_err_string);
417 }
418 }
419 if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
420 $notes = @{$msg_hash->{'notes'}}[0];
421 $callobj = {
422 method => 'setHostNotes',
423 params => [ $hostId, $notes ],
424 id => 1,
425 };
426 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
427 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
428 if ($sres_err){
429 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
430 &add_content2xml_hash($out_hash, "error", $sres_err_string);
431 }
432 }
433 if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
434 $mac = @{$msg_hash->{'mac'}}[0];
435 $callobj = {
436 method => 'setMacAddress',
437 params => [ $hostId, $mac ],
438 id => 1,
439 };
440 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
441 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
442 if ($sres_err){
443 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
444 &add_content2xml_hash($out_hash, "error", $sres_err_string);
445 }
446 }
447 }
449 # Return message
450 return ( &create_xml_string($out_hash) );
451 }
454 ## @method opsi_get_netboot_products
455 # Get netboot products for specific host.
456 # @param msg - STRING - xml message with tag hostId
457 # @param msg_hash - HASHREF - message information parsed into a hash
458 # @param session_id - INTEGER - POE session id of the processing of this message
459 # @return out_msg - STRING - feedback to GOsa in success and error case
460 sub opsi_get_netboot_products {
461 my ($msg, $msg_hash, $session_id) = @_;
462 my $header = @{$msg_hash->{'header'}}[0];
463 my $source = @{$msg_hash->{'source'}}[0];
464 my $target = @{$msg_hash->{'target'}}[0];
465 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
466 my $hostId;
467 my $xml_msg;
469 # Build return message with twisted target and source
470 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
471 if (defined $forward_to_gosa) {
472 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
473 }
475 # Get hostId if defined
476 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
477 $hostId = @{$msg_hash->{'hostId'}}[0];
478 &add_content2xml_hash($out_hash, "hostId", $hostId);
479 }
481 &add_content2xml_hash($out_hash, "xxx", "");
482 $xml_msg = &create_xml_string($out_hash);
483 # For hosts, only return the products that are or get installed
484 my $callobj;
485 $callobj = {
486 method => 'getNetBootProductIds_list',
487 params => [ ],
488 id => 1,
489 };
490 &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
491 &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
492 &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
493 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
494 &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
495 my %r = ();
496 for (@{$res->result}) { $r{$_} = 1 }
498 if (not &check_opsi_res($res)){
500 if (defined $hostId){
502 $callobj = {
503 method => 'getProductStates_hash',
504 params => [ $hostId ],
505 id => 1,
506 };
508 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
509 if (not &check_opsi_res($hres)){
510 my $htmp= $hres->result->{$hostId};
512 # check state != not_installed or action == setup -> load and add
513 foreach my $product (@{$htmp}){
515 if (!defined ($r{$product->{'productId'}})){
516 next;
517 }
519 # Now we've a couple of hashes...
520 if ($product->{'installationStatus'} ne "not_installed" or
521 $product->{'actionRequest'} eq "setup"){
522 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
524 $callobj = {
525 method => 'getProduct_hash',
526 params => [ $product->{'productId'} ],
527 id => 1,
528 };
530 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
531 if (not &check_opsi_res($sres)){
532 my $tres= $sres->result;
534 my $name= xml_quote($tres->{'name'});
535 my $r= $product->{'productId'};
536 my $description= xml_quote($tres->{'description'});
537 $name=~ s/\//\\\//;
538 $description=~ s/\//\\\//;
539 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
540 }
541 }
542 }
544 }
546 } else {
547 foreach my $r (@{$res->result}) {
548 $callobj = {
549 method => 'getProduct_hash',
550 params => [ $r ],
551 id => 1,
552 };
554 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
555 if (not &check_opsi_res($sres)){
556 my $tres= $sres->result;
558 my $name= xml_quote($tres->{'name'});
559 my $description= xml_quote($tres->{'description'});
560 $name=~ s/\//\\\//;
561 $description=~ s/\//\\\//;
562 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
563 }
564 }
566 }
567 }
568 $xml_msg=~ s/<xxx><\/xxx>//;
570 # Return message
571 return ( $xml_msg );
572 }
575 ## @method opsi_get_product_properties
576 # Get product properties for a product and a specific host or gobally for a product.
577 # @param msg - STRING - xml message with tags productId and optional hostId
578 # @param msg_hash - HASHREF - message information parsed into a hash
579 # @param session_id - INTEGER - POE session id of the processing of this message
580 # @return out_msg - STRING - feedback to GOsa in success and error case
581 sub opsi_get_product_properties {
582 my ($msg, $msg_hash, $session_id) = @_;
583 my $header = @{$msg_hash->{'header'}}[0];
584 my $source = @{$msg_hash->{'source'}}[0];
585 my $target = @{$msg_hash->{'target'}}[0];
586 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
587 my ($hostId, $productId);
588 my $xml_msg;
590 # Build return message with twisted target and source
591 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
592 if (defined $forward_to_gosa) {
593 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
594 }
596 # Sanity check of needed parameter
597 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
598 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
599 &add_content2xml_hash($out_hash, "error", "productId");
600 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
602 # Return message
603 return ( &create_xml_string($out_hash) );
604 }
606 # Get productid
607 $productId = @{$msg_hash->{'productId'}}[0];
608 &add_content2xml_hash($out_hash, "producId", "$productId");
610 # Get hostId if defined
611 if (defined @{$msg_hash->{'hostId'}}[0]){
612 $hostId = @{$msg_hash->{'hostId'}}[0];
613 &add_content2xml_hash($out_hash, "hostId", $hostId);
614 }
616 # Load actions
617 my $callobj = {
618 method => 'getPossibleProductActions_list',
619 params => [ $productId ],
620 id => 1,
621 };
622 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
623 if (not &check_opsi_res($res)){
624 foreach my $action (@{$res->result}){
625 &add_content2xml_hash($out_hash, "action", $action);
626 }
627 }
629 # Add place holder
630 &add_content2xml_hash($out_hash, "xxx", "");
632 # Move to XML string
633 $xml_msg= &create_xml_string($out_hash);
635 # JSON Query
636 if (defined $hostId){
637 $callobj = {
638 method => 'getProductProperties_hash',
639 params => [ $productId, $hostId ],
640 id => 1,
641 };
642 } else {
643 $callobj = {
644 method => 'getProductProperties_hash',
645 params => [ $productId ],
646 id => 1,
647 };
648 }
649 $res = $main::opsi_client->call($main::opsi_url, $callobj);
651 # JSON Query 2
652 $callobj = {
653 method => 'getProductPropertyDefinitions_listOfHashes',
654 params => [ $productId ],
655 id => 1,
656 };
658 # Assemble options
659 my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
660 my $values = {};
661 my $descriptions = {};
662 if (not &check_opsi_res($res2)){
663 my $r= $res2->result;
665 foreach my $entr (@$r){
666 # Unroll values
667 my $cnv;
668 if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
669 foreach my $v (@{$entr->{'values'}}){
670 $cnv.= "<value>$v</value>";
671 }
672 } else {
673 $cnv= $entr->{'values'};
674 }
675 $values->{$entr->{'name'}}= $cnv;
676 $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
677 }
678 }
680 if (not &check_opsi_res($res)){
681 my $r= $res->result;
682 foreach my $key (keys %{$r}) {
683 my $item= "\n<item>";
684 my $value= $r->{$key};
685 my $dsc= "";
686 my $vals= "";
687 if (defined $descriptions->{$key}){
688 $dsc= $descriptions->{$key};
689 }
690 if (defined $values->{$key}){
691 $vals= $values->{$key};
692 }
693 $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
694 $item.= "</item>";
695 $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
696 }
697 }
699 $xml_msg=~ s/<xxx><\/xxx>//;
701 # Return message
702 return ( $xml_msg );
703 }
706 ## @method opsi_set_product_properties
707 # 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.
708 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
709 # @param msg_hash - HASHREF - message information parsed into a hash
710 # @param session_id - INTEGER - POE session id of the processing of this message
711 # @return out_msg - STRING - feedback to GOsa in success and error case
712 sub opsi_set_product_properties {
713 my ($msg, $msg_hash, $session_id) = @_;
714 my $header = @{$msg_hash->{'header'}}[0];
715 my $source = @{$msg_hash->{'source'}}[0];
716 my $target = @{$msg_hash->{'target'}}[0];
717 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
718 my ($productId, $hostId);
720 # Build return message with twisted target and source
721 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
722 if (defined $forward_to_gosa) {
723 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
724 }
726 # Sanity check of needed parameter
727 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
728 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
729 &add_content2xml_hash($out_hash, "error", "productId");
730 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
731 return ( &create_xml_string($out_hash) );
732 }
733 if (not exists $msg_hash->{'item'}) {
734 &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
735 &add_content2xml_hash($out_hash, "error", "item");
736 &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1);
737 return ( &create_xml_string($out_hash) );
738 } else {
739 if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
740 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
741 &add_content2xml_hash($out_hash, "error", "name");
742 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1);
743 return ( &create_xml_string($out_hash) );
744 }
745 if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
746 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
747 &add_content2xml_hash($out_hash, "error", "value");
748 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1);
749 return ( &create_xml_string($out_hash) );
750 }
751 }
752 # if no hostId is given, set_product_properties will act on globally
753 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1)) {
754 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
755 &add_content2xml_hash($out_hash, "error", "hostId");
756 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
757 return ( &create_xml_string($out_hash) );
758 }
761 # Get productId
762 $productId = @{$msg_hash->{'productId'}}[0];
763 &add_content2xml_hash($out_hash, "productId", $productId);
765 # Get hostId if defined
766 if (exists $msg_hash->{'hostId'}){
767 $hostId = @{$msg_hash->{'hostId'}}[0];
768 &add_content2xml_hash($out_hash, "hostId", $hostId);
769 }
771 # Set product states if requested
772 if (defined @{$msg_hash->{'action'}}[0]){
773 &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
774 }
775 if (defined @{$msg_hash->{'state'}}[0]){
776 &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
777 }
779 # Find properties
780 foreach my $item (@{$msg_hash->{'item'}}){
781 # JSON Query
782 my $callobj;
784 if (defined $hostId){
785 $callobj = {
786 method => 'setProductProperty',
787 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
788 id => 1,
789 };
790 } else {
791 $callobj = {
792 method => 'setProductProperty',
793 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
794 id => 1,
795 };
796 }
798 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
799 my ($res_err, $res_err_string) = &check_opsi_res($res);
801 if ($res_err){
802 &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
803 &add_content2xml_hash($out_hash, "error", $res_err_string);
804 }
805 }
808 # Return message
809 return ( &create_xml_string($out_hash) );
810 }
813 ## @method opsi_get_client_hardware
814 # Reports client hardware inventory.
815 # @param msg - STRING - xml message with tag hostId
816 # @param msg_hash - HASHREF - message information parsed into a hash
817 # @param session_id - INTEGER - POE session id of the processing of this message
818 # @return out_msg - STRING - feedback to GOsa in success and error case
819 sub opsi_get_client_hardware {
820 my ($msg, $msg_hash, $session_id) = @_;
821 my $header = @{$msg_hash->{'header'}}[0];
822 my $source = @{$msg_hash->{'source'}}[0];
823 my $target = @{$msg_hash->{'target'}}[0];
824 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
825 my $hostId;
826 my $error = 0;
827 my $xml_msg;
829 # Build return message with twisted target and source
830 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
831 if (defined $forward_to_gosa) {
832 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
833 }
835 # Sanity check of needed parameter
836 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
837 $error++;
838 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
839 &add_content2xml_hash($out_hash, "error", "hostId");
840 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
841 }
843 if (not $error) {
845 # Get hostId
846 $hostId = @{$msg_hash->{'hostId'}}[0];
847 &add_content2xml_hash($out_hash, "hostId", "$hostId");
848 &add_content2xml_hash($out_hash, "xxx", "");
849 }
851 # Move to XML string
852 $xml_msg= &create_xml_string($out_hash);
854 if (not $error) {
856 # JSON Query
857 my $callobj = {
858 method => 'getHardwareInformation_hash',
859 params => [ $hostId ],
860 id => 1,
861 };
863 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
864 if (not &check_opsi_res($res)){
865 my $result= $res->result;
866 if (ref $result eq "HASH") {
867 foreach my $r (keys %{$result}){
868 my $item= "\n<item><id>".xml_quote($r)."</id>";
869 my $value= $result->{$r};
870 foreach my $sres (@{$value}){
872 foreach my $dres (keys %{$sres}){
873 if (defined $sres->{$dres}){
874 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
875 }
876 }
878 }
879 $item.= "</item>";
880 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
882 }
883 }
884 }
886 $xml_msg=~ s/<xxx><\/xxx>//;
888 }
890 # Return message
891 return ( $xml_msg );
892 }
895 ## @method opsi_list_clients
896 # Reports all Opsi clients.
897 # @param msg - STRING - xml message
898 # @param msg_hash - HASHREF - message information parsed into a hash
899 # @param session_id - INTEGER - POE session id of the processing of this message
900 # @return out_msg - STRING - feedback to GOsa in success and error case
901 sub opsi_list_clients {
902 my ($msg, $msg_hash, $session_id) = @_;
903 my $header = @{$msg_hash->{'header'}}[0];
904 my $source = @{$msg_hash->{'source'}}[0];
905 my $target = @{$msg_hash->{'target'}}[0];
906 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
908 # Build return message with twisted target and source
909 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
910 if (defined $forward_to_gosa) {
911 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
912 }
913 &add_content2xml_hash($out_hash, "xxx", "");
915 # Move to XML string
916 my $xml_msg= &create_xml_string($out_hash);
918 # JSON Query
919 my $callobj = {
920 method => 'getClients_listOfHashes',
921 params => [ ],
922 id => 1,
923 };
924 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
925 if (not &check_opsi_res($res)){
926 foreach my $host (@{$res->result}){
927 my $item= "\n<item><name>".$host->{'hostId'}."</name>";
928 if (defined($host->{'description'})){
929 $item.= "<description>".xml_quote($host->{'description'})."</description>";
930 }
931 if (defined($host->{'notes'})){
932 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
933 }
934 if (defined($host->{'lastSeen'})){
935 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
936 }
938 $callobj = {
939 method => 'getIpAddress',
940 params => [ $host->{'hostId'} ],
941 id => 1,
942 };
943 my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
944 if ( not &check_opsi_res($sres)){
945 $item.= "<ip>".xml_quote($sres->result)."</ip>";
946 }
948 $callobj = {
949 method => 'getMacAddress',
950 params => [ $host->{'hostId'} ],
951 id => 1,
952 };
953 $sres= $main::opsi_client->call($main::opsi_url, $callobj);
954 if ( not &check_opsi_res($sres)){
955 $item.= "<mac>".xml_quote($sres->result)."</mac>";
956 }
957 $item.= "</item>";
958 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
959 }
960 }
962 $xml_msg=~ s/<xxx><\/xxx>//;
963 return ( $xml_msg );
964 }
968 ## @method opsi_get_client_software
969 # Reports client software inventory.
970 # @param msg - STRING - xml message with tag hostId
971 # @param msg_hash - HASHREF - message information parsed into a hash
972 # @param session_id - INTEGER - POE session id of the processing of this message
973 # @return out_msg - STRING - feedback to GOsa in success and error case
974 sub opsi_get_client_software {
975 my ($msg, $msg_hash, $session_id) = @_;
976 my $header = @{$msg_hash->{'header'}}[0];
977 my $source = @{$msg_hash->{'source'}}[0];
978 my $target = @{$msg_hash->{'target'}}[0];
979 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
980 my $error = 0;
981 my $hostId;
982 my $xml_msg;
984 # Build return message with twisted target and source
985 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
986 if (defined $forward_to_gosa) {
987 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
988 }
990 # Sanity check of needed parameter
991 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
992 $error++;
993 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
994 &add_content2xml_hash($out_hash, "error", "hostId");
995 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
996 }
998 if (not $error) {
1000 # Get hostId
1001 $hostId = @{$msg_hash->{'hostId'}}[0];
1002 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1003 &add_content2xml_hash($out_hash, "xxx", "");
1004 }
1006 $xml_msg= &create_xml_string($out_hash);
1008 if (not $error) {
1010 # JSON Query
1011 my $callobj = {
1012 method => 'getSoftwareInformation_hash',
1013 params => [ $hostId ],
1014 id => 1,
1015 };
1017 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1018 if (not &check_opsi_res($res)){
1019 my $result= $res->result;
1020 }
1022 $xml_msg=~ s/<xxx><\/xxx>//;
1024 }
1026 # Return message
1027 return ( $xml_msg );
1028 }
1031 ## @method opsi_get_local_products
1032 # Reports product for given hostId or globally.
1033 # @param msg - STRING - xml message with optional tag hostId
1034 # @param msg_hash - HASHREF - message information parsed into a hash
1035 # @param session_id - INTEGER - POE session id of the processing of this message
1036 # @return out_msg - STRING - feedback to GOsa in success and error case
1037 sub opsi_get_local_products {
1038 my ($msg, $msg_hash, $session_id) = @_;
1039 my $header = @{$msg_hash->{'header'}}[0];
1040 my $source = @{$msg_hash->{'source'}}[0];
1041 my $target = @{$msg_hash->{'target'}}[0];
1042 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1043 my $hostId;
1045 # Build return message with twisted target and source
1046 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1047 if (defined $forward_to_gosa) {
1048 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1049 }
1050 &add_content2xml_hash($out_hash, "xxx", "");
1052 # Get hostId if defined
1053 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
1054 $hostId = @{$msg_hash->{'hostId'}}[0];
1055 &add_content2xml_hash($out_hash, "hostId", $hostId);
1056 }
1058 # Move to XML string
1059 my $xml_msg= &create_xml_string($out_hash);
1061 # For hosts, only return the products that are or get installed
1062 my $callobj;
1063 $callobj = {
1064 method => 'getLocalBootProductIds_list',
1065 params => [ ],
1066 id => 1,
1067 };
1069 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1070 my %r = ();
1071 for (@{$res->result}) { $r{$_} = 1 }
1073 if (not &check_opsi_res($res)){
1075 if (defined $hostId){
1076 $callobj = {
1077 method => 'getProductStates_hash',
1078 params => [ $hostId ],
1079 id => 1,
1080 };
1082 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1083 if (not &check_opsi_res($hres)){
1084 my $htmp= $hres->result->{$hostId};
1086 # Check state != not_installed or action == setup -> load and add
1087 foreach my $product (@{$htmp}){
1089 if (!defined ($r{$product->{'productId'}})){
1090 next;
1091 }
1093 # Now we've a couple of hashes...
1094 if ($product->{'installationStatus'} ne "not_installed" or
1095 $product->{'actionRequest'} eq "setup"){
1096 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1098 $callobj = {
1099 method => 'getProduct_hash',
1100 params => [ $product->{'productId'} ],
1101 id => 1,
1102 };
1104 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1105 if (not &check_opsi_res($sres)){
1106 my $tres= $sres->result;
1108 my $name= xml_quote($tres->{'name'});
1109 my $r= $product->{'productId'};
1110 my $description= xml_quote($tres->{'description'});
1111 $name=~ s/\//\\\//;
1112 $description=~ s/\//\\\//;
1113 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1114 }
1116 }
1117 }
1119 }
1121 } else {
1122 foreach my $r (@{$res->result}) {
1123 $callobj = {
1124 method => 'getProduct_hash',
1125 params => [ $r ],
1126 id => 1,
1127 };
1129 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1130 if (not &check_opsi_res($sres)){
1131 my $tres= $sres->result;
1133 my $name= xml_quote($tres->{'name'});
1134 my $description= xml_quote($tres->{'description'});
1135 $name=~ s/\//\\\//;
1136 $description=~ s/\//\\\//;
1137 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1138 }
1140 }
1142 }
1143 }
1145 $xml_msg=~ s/<xxx><\/xxx>//;
1147 # Retrun Message
1148 return ( $xml_msg );
1149 }
1152 ## @method opsi_del_client
1153 # Deletes a client from Opsi.
1154 # @param msg - STRING - xml message with tag hostId
1155 # @param msg_hash - HASHREF - message information parsed into a hash
1156 # @param session_id - INTEGER - POE session id of the processing of this message
1157 # @return out_msg - STRING - feedback to GOsa in success and error case
1158 sub opsi_del_client {
1159 my ($msg, $msg_hash, $session_id) = @_;
1160 my $header = @{$msg_hash->{'header'}}[0];
1161 my $source = @{$msg_hash->{'source'}}[0];
1162 my $target = @{$msg_hash->{'target'}}[0];
1163 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1164 my $hostId;
1165 my $error = 0;
1167 # Build return message with twisted target and source
1168 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1169 if (defined $forward_to_gosa) {
1170 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1171 }
1173 # Sanity check of needed parameter
1174 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1175 $error++;
1176 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1177 &add_content2xml_hash($out_hash, "error", "hostId");
1178 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
1179 }
1181 if (not $error) {
1183 # Get hostId
1184 $hostId = @{$msg_hash->{'hostId'}}[0];
1185 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1187 # JSON Query
1188 my $callobj = {
1189 method => 'deleteClient',
1190 params => [ $hostId ],
1191 id => 1,
1192 };
1193 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1194 }
1196 # Move to XML string
1197 my $xml_msg= &create_xml_string($out_hash);
1199 # Return message
1200 return ( $xml_msg );
1201 }
1204 ## @method opsi_install_client
1205 # Set a client in Opsi to install and trigger a wake on lan message (WOL).
1206 # @param msg - STRING - xml message with tags hostId, macaddress
1207 # @param msg_hash - HASHREF - message information parsed into a hash
1208 # @param session_id - INTEGER - POE session id of the processing of this message
1209 # @return out_msg - STRING - feedback to GOsa in success and error case
1210 sub opsi_install_client {
1211 my ($msg, $msg_hash, $session_id) = @_;
1212 my $header = @{$msg_hash->{'header'}}[0];
1213 my $source = @{$msg_hash->{'source'}}[0];
1214 my $target = @{$msg_hash->{'target'}}[0];
1215 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1218 my ($hostId, $macaddress);
1220 my $error = 0;
1221 my @out_msg_l;
1223 # Build return message with twisted target and source
1224 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1225 if (defined $forward_to_gosa) {
1226 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1227 }
1229 # Sanity check of needed parameter
1230 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1231 $error++;
1232 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1233 &add_content2xml_hash($out_hash, "error", "hostId");
1234 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
1235 }
1236 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') ) {
1237 $error++;
1238 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1239 &add_content2xml_hash($out_hash, "error", "macaddress");
1240 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
1241 } else {
1242 if ((exists $msg_hash->{'macaddress'}) &&
1243 ($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)) {
1244 $macaddress = $1;
1245 } else {
1246 $error ++;
1247 &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1248 &add_content2xml_hash($out_hash, "error", "macaddress");
1249 &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1);
1250 }
1251 }
1253 if (not $error) {
1255 # Get hostId
1256 $hostId = @{$msg_hash->{'hostId'}}[0];
1257 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1259 # Load all products for this host with status != "not_installed" or actionRequest != "none"
1260 my $callobj = {
1261 method => 'getProductStates_hash',
1262 params => [ $hostId ],
1263 id => 1,
1264 };
1266 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1267 if (not &check_opsi_res($hres)){
1268 my $htmp= $hres->result->{$hostId};
1270 # check state != not_installed or action == setup -> load and add
1271 foreach my $product (@{$htmp}){
1272 # Now we've a couple of hashes...
1273 if ($product->{'installationStatus'} ne "not_installed" or
1274 $product->{'actionRequest'} ne "none"){
1276 # Do an action request for all these -> "setup".
1277 $callobj = {
1278 method => 'setProductActionRequest',
1279 params => [ $product->{'productId'}, $hostId, "setup" ],
1280 id => 1,
1281 };
1282 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1283 my ($res_err, $res_err_string) = &check_opsi_res($res);
1284 if ($res_err){
1285 &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1286 } else {
1287 &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1288 }
1289 }
1290 }
1291 }
1292 push(@out_msg_l, &create_xml_string($out_hash));
1295 # Build wakeup message for client
1296 if (not $error) {
1297 my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1298 &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1299 my $wakeup_msg = &create_xml_string($wakeup_hash);
1300 push(@out_msg_l, $wakeup_msg);
1302 # invoke trigger wake for this gosa-si-server
1303 &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1304 }
1305 }
1307 # Return messages
1308 return @out_msg_l;
1309 }
1312 ## @method _set_action
1313 # Set action for an Opsi client
1314 # @param product - STRING - Opsi product
1315 # @param action - STRING - action
1316 # @param hostId - STRING - Opsi hostId
1317 sub _set_action {
1318 my $product= shift;
1319 my $action = shift;
1320 my $hostId = shift;
1321 my $callobj;
1323 $callobj = {
1324 method => 'setProductActionRequest',
1325 params => [ $product, $hostId, $action],
1326 id => 1,
1327 };
1329 $main::opsi_client->call($main::opsi_url, $callobj);
1330 }
1332 ## @method _set_state
1333 # Set state for an Opsi client
1334 # @param product - STRING - Opsi product
1335 # @param action - STRING - state
1336 # @param hostId - STRING - Opsi hostId
1337 sub _set_state {
1338 my $product = shift;
1339 my $state = shift;
1340 my $hostId = shift;
1341 my $callobj;
1343 $callobj = {
1344 method => 'setProductState',
1345 params => [ $product, $hostId, $state ],
1346 id => 1,
1347 };
1349 $main::opsi_client->call($main::opsi_url, $callobj);
1350 }
1352 ################################
1353 #
1354 # @brief Create a license pool at Opsi server.
1355 # @param licensePoolId The name of the pool (optional).
1356 # @param description The description of the pool (optional).
1357 # @param productIds A list of assigned porducts of the pool (optional).
1358 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional).
1359 #
1360 sub opsi_createLicensePool {
1361 my ($msg, $msg_hash, $session_id) = @_;
1362 my $header = @{$msg_hash->{'header'}}[0];
1363 my $source = @{$msg_hash->{'source'}}[0];
1364 my $target = @{$msg_hash->{'target'}}[0];
1365 my $out_hash;
1366 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1367 my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1368 my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1369 my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1371 # Create license Pool
1372 my $callobj = {
1373 method => 'createLicensePool',
1374 params => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1375 id => 1,
1376 };
1377 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1379 # Check Opsi error
1380 my ($res_error, $res_error_str) = &check_opsi_res($res);
1381 if ($res_error){
1382 # Create error message
1383 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1384 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1385 return ( &create_xml_string($out_hash) );
1386 }
1388 # Create function result message
1389 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1391 return ( &create_xml_string($out_hash) );
1392 }
1394 ################################
1395 #
1396 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1397 #
1398 sub opsi_getLicensePools_listOfHashes {
1399 my ($msg, $msg_hash, $session_id) = @_;
1400 my $header = @{$msg_hash->{'header'}}[0];
1401 my $source = @{$msg_hash->{'source'}}[0];
1402 my $out_hash;
1404 # Fetch infos from Opsi server
1405 my $callobj = {
1406 method => 'getLicensePools_listOfHashes',
1407 params => [ ],
1408 id => 1,
1409 };
1410 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1412 # Check Opsi error
1413 my ($res_error, $res_error_str) = &check_opsi_res($res);
1414 if ($res_error){
1415 # Create error message
1416 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1417 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1418 return ( &create_xml_string($out_hash) );
1419 }
1421 # Create function result message
1422 my $res_hash = { 'hit'=> [] };
1423 foreach my $licensePool ( @{$res->result}) {
1424 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1425 'description' => [$licensePool->{'description'}],
1426 'productIds' => $licensePool->{'productIds'},
1427 'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1428 };
1429 push( @{$res_hash->{hit}}, $licensePool_hash );
1430 }
1432 # Create function result message
1433 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1434 $out_hash->{result} = [$res_hash];
1436 return ( &create_xml_string($out_hash) );
1437 }
1439 ################################
1440 #
1441 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1442 # @param licensePoolId The name of the pool.
1443 #
1444 sub opsi_getLicensePool_hash {
1445 my ($msg, $msg_hash, $session_id) = @_;
1446 my $header = @{$msg_hash->{'header'}}[0];
1447 my $source = @{$msg_hash->{'source'}}[0];
1448 my $target = @{$msg_hash->{'target'}}[0];
1449 my $licensePoolId;
1450 my $out_hash;
1452 # Check input sanity
1453 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1454 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1455 } else {
1456 return ( &_giveErrorFeedback($msg_hash, "", $session_id, $_) );
1457 }
1459 # Fetch infos from Opsi server
1460 my $callobj = {
1461 method => 'getLicensePool_hash',
1462 params => [ $licensePoolId ],
1463 id => 1,
1464 };
1465 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1467 # Check Opsi error
1468 my ($res_error, $res_error_str) = &check_opsi_res($res);
1469 if ($res_error){
1470 # Create error message
1471 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1472 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1473 &add_content2xml_hash($out_hash, "error", $res_error_str);
1474 return ( &create_xml_string($out_hash) );
1475 }
1477 # Create function result message
1478 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1479 &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1480 &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1481 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1482 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1484 return ( &create_xml_string($out_hash) );
1485 }
1487 sub _parse_getSoftwareLicenseUsages {
1488 my $res = shift;
1490 # Parse Opsi result
1491 my $tmp_licensePool_cache = {};
1492 my $res_hash = { 'hit'=> [] };
1493 foreach my $license ( @{$res}) {
1494 my $tmp_licensePool = $license->{'licensePoolId'};
1495 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1496 # Fetch missing informations from Opsi and cache the results for a possible later usage
1497 my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1498 if (not $err) {
1499 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1500 }
1501 }
1502 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1503 'notes' => [$license->{'notes'}],
1504 'licenseKey' => [$license->{'licenseKey'}],
1505 'hostId' => [$license->{'hostId'}],
1506 'licensePoolId' => [$tmp_licensePool],
1507 };
1508 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1509 $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1510 map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1511 map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1512 }
1513 push( @{$res_hash->{hit}}, $license_hash );
1514 }
1516 return $res_hash;
1517 }
1519 ################################
1520 #
1521 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1522 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1523 # @param licensePoolId The name of the pool (optional).
1524 #
1525 sub opsi_getSoftwareLicenseUsages {
1526 my ($msg, $msg_hash, $session_id) = @_;
1527 my $header = @{$msg_hash->{'header'}}[0];
1528 my $source = @{$msg_hash->{'source'}}[0];
1529 my $target = @{$msg_hash->{'target'}}[0];
1530 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1531 my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1532 my $out_hash;
1534 my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1535 if ($err){
1536 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1537 }
1539 # Parse Opsi result
1540 my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1542 # Create function result message
1543 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1544 $out_hash->{result} = [$res_hash];
1546 return ( &create_xml_string($out_hash) );
1547 }
1549 ################################
1550 #
1551 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1552 # @param productId Something like 'firefox', 'python' or anything else .
1553 #
1554 sub opsi_getSoftwareLicenseUsagesForProductId {
1555 my ($msg, $msg_hash, $session_id) = @_;
1556 my $header = @{$msg_hash->{'header'}}[0];
1557 my $source = @{$msg_hash->{'source'}}[0];
1559 # Check input sanity
1560 my $productId;
1561 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1562 $productId= @{$msg_hash->{'productId'}}[0];
1563 } else {
1564 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1565 }
1567 # Fetch licensePoolId for productId
1568 my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1569 if ($err){
1570 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1571 }
1573 my $licensePoolId;
1575 # Fetch softwareLiceceUsages for licensePoolId
1576 ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1577 if ($err){
1578 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1579 }
1581 # Parse Opsi result
1582 my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1584 # Create function result message
1585 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1586 $out_hash->{result} = [$res_hash];
1588 return ( &create_xml_string($out_hash) );
1589 }
1591 ################################
1592 #
1593 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1594 # @param softwareLicenseId Identificator of a license.
1595 #
1596 sub opsi_getSoftwareLicense_hash {
1597 my ($msg, $msg_hash, $session_id) = @_;
1598 my $header = @{$msg_hash->{'header'}}[0];
1599 my $source = @{$msg_hash->{'source'}}[0];
1600 my $target = @{$msg_hash->{'target'}}[0];
1601 my $softwareLicenseId;
1602 my $out_hash;
1604 # Check input sanity
1605 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1606 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1607 } else {
1608 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1609 }
1611 my $callobj = {
1612 method => 'getSoftwareLicense_hash',
1613 params => [ $softwareLicenseId ],
1614 id => 1,
1615 };
1616 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1618 # Check Opsi error
1619 my ($res_error, $res_error_str) = &check_opsi_res($res);
1620 if ($res_error){
1621 # Create error message
1622 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1623 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1624 return ( &create_xml_string($out_hash) );
1625 }
1627 # Create function result message
1628 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1629 &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1630 &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1631 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1632 &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1633 foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1634 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1635 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1636 }
1638 return ( &create_xml_string($out_hash) );
1639 }
1641 ################################
1642 #
1643 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool.
1644 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted.
1645 # @param licensePoolId The name of the pool.
1646 #
1647 sub opsi_deleteLicensePool {
1648 my ($msg, $msg_hash, $session_id) = @_;
1649 my $header = @{$msg_hash->{'header'}}[0];
1650 my $source = @{$msg_hash->{'source'}}[0];
1651 my $target = @{$msg_hash->{'target'}}[0];
1652 my $licensePoolId;
1653 my $out_hash;
1655 # Check input sanity
1656 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1657 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1658 } else {
1659 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1660 }
1662 # Fetch softwareLicenseIds used in license pool
1663 # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1664 # but not the license contracts of the software licenses. In our case each software license has exactly one license contract.
1665 my $callobj = {
1666 method => 'getSoftwareLicenses_listOfHashes',
1667 params => [ ],
1668 id => 1,
1669 };
1670 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1672 # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1673 my @lCI_toBeDeleted;
1674 foreach my $softwareLicenseHash ( @{$res->result} ) {
1675 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) {
1676 next;
1677 }
1678 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1679 }
1681 # Delete license pool at Opsi server
1682 $callobj = {
1683 method => 'deleteLicensePool',
1684 params => [ $licensePoolId, 'deleteLicenses=True' ],
1685 id => 1,
1686 };
1687 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1688 my ($res_error, $res_error_str) = &check_opsi_res($res);
1689 if ($res_error){
1690 # Create error message
1691 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1692 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1693 return ( &create_xml_string($out_hash) );
1694 }
1696 # Delete each license contract connected with the license pool
1697 foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1698 my $callobj = {
1699 method => 'deleteLicenseContract',
1700 params => [ $licenseContractId ],
1701 id => 1,
1702 };
1703 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1704 my ($res_error, $res_error_str) = &check_opsi_res($res);
1705 if ($res_error){
1706 # Create error message
1707 &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1708 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1709 return ( &create_xml_string($out_hash) );
1710 }
1711 }
1713 # Create function result message
1714 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1716 return ( &create_xml_string($out_hash) );
1717 }
1719 ################################
1720 #
1721 # @brief Create a license contract, create a software license and add the software license to the license pool
1722 # @param licensePoolId The name of the pool the license should be assigned.
1723 # @param licenseKey The license key.
1724 # @param partner Name of the license partner (optional).
1725 # @param conclusionDate Date of conclusion of license contract (optional)
1726 # @param notificationDate Date of notification that license is running out soon (optional).
1727 # @param notes This is the place for some notes (optional)
1728 # @param softwareLicenseId Identificator of a license (optional).
1729 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1730 # @param maxInstallations The number of clients use this license (optional).
1731 # @param boundToHost The name of the client the license is bound to (optional).
1732 # @param expirationDate The date when the license is running down (optional).
1733 #
1734 sub opsi_createLicense {
1735 my ($msg, $msg_hash, $session_id) = @_;
1736 my $header = @{$msg_hash->{'header'}}[0];
1737 my $source = @{$msg_hash->{'source'}}[0];
1738 my $target = @{$msg_hash->{'target'}}[0];
1739 my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1740 my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1741 my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1742 my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1743 my $licenseContractId = undef;
1744 my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1745 my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1746 my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1747 my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1748 my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1749 my $licensePoolId;
1750 my $licenseKey;
1751 my $out_hash;
1753 # Check input sanity
1754 if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1755 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1756 } else {
1757 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1758 }
1759 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1760 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1761 } else {
1762 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1763 }
1764 if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1765 return ( &_give_feedback($msg, $msg_hash, $session_id, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'."));
1766 }
1768 # Automatically define licenseContractId if ID is not given
1769 if (defined $softwareLicenseId) {
1770 $licenseContractId = "c_".$softwareLicenseId;
1771 }
1773 # Create license contract at Opsi server
1774 my $callobj = {
1775 method => 'createLicenseContract',
1776 params => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1777 id => 1,
1778 };
1779 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1781 # Check Opsi error
1782 my ($res_error, $res_error_str) = &check_opsi_res($res);
1783 if ($res_error){
1784 # Create error message
1785 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1786 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1787 return ( &create_xml_string($out_hash) );
1788 }
1790 $licenseContractId = $res->result;
1792 # Create software license at Opsi server
1793 $callobj = {
1794 method => 'createSoftwareLicense',
1795 params => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1796 id => 1,
1797 };
1798 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1800 # Check Opsi error
1801 ($res_error, $res_error_str) = &check_opsi_res($res);
1802 if ($res_error){
1803 # Create error message
1804 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1805 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1806 return ( &create_xml_string($out_hash) );
1807 }
1809 $softwareLicenseId = $res->result;
1811 # Add software license to license pool
1812 $callobj = {
1813 method => 'addSoftwareLicenseToLicensePool',
1814 params => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1815 id => 1,
1816 };
1817 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1819 # Check Opsi error
1820 ($res_error, $res_error_str) = &check_opsi_res($res);
1821 if ($res_error){
1822 # Create error message
1823 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1824 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1825 return ( &create_xml_string($out_hash) );
1826 }
1828 # Create function result message
1829 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1831 return ( &create_xml_string($out_hash) );
1832 }
1834 ################################
1835 #
1836 # @brief Assign a software license to a host
1837 # @param hostid Something like client_1.intranet.mydomain.de
1838 # @param licensePoolId The name of the pool.
1839 #
1840 sub opsi_assignSoftwareLicenseToHost {
1841 my ($msg, $msg_hash, $session_id) = @_;
1842 my $header = @{$msg_hash->{'header'}}[0];
1843 my $source = @{$msg_hash->{'source'}}[0];
1844 my $target = @{$msg_hash->{'target'}}[0];
1845 my $hostId;
1846 my $licensePoolId;
1848 # Check input sanity
1849 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1850 $hostId = @{$msg_hash->{'hostId'}}[0];
1851 } else {
1852 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1853 }
1854 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1855 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1856 } else {
1857 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1858 }
1860 # Assign a software license to a host
1861 my $callobj = {
1862 method => 'getAndAssignSoftwareLicenseKey',
1863 params => [ $hostId, $licensePoolId ],
1864 id => 1,
1865 };
1866 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1868 # Check Opsi error
1869 my ($res_error, $res_error_str) = &check_opsi_res($res);
1870 if ($res_error){
1871 # Create error message
1872 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1873 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1874 return ( &create_xml_string($out_hash) );
1875 }
1877 # Create function result message
1878 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1880 return ( &create_xml_string($out_hash) );
1881 }
1883 ################################
1884 #
1885 # @brief Unassign a software license from a host.
1886 # @param hostid Something like client_1.intranet.mydomain.de
1887 # @param licensePoolId The name of the pool.
1888 #
1889 sub opsi_unassignSoftwareLicenseFromHost {
1890 my ($msg, $msg_hash, $session_id) = @_;
1891 my $header = @{$msg_hash->{'header'}}[0];
1892 my $source = @{$msg_hash->{'source'}}[0];
1893 my $target = @{$msg_hash->{'target'}}[0];
1894 my $hostId;
1895 my $licensePoolId;
1897 # Check input sanity
1898 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1899 $hostId = @{$msg_hash->{'hostId'}}[0];
1900 } else {
1901 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1902 }
1903 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1904 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1905 } else {
1906 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1907 }
1909 # Unassign a software license from a host
1910 my $callobj = {
1911 method => 'deleteSoftwareLicenseUsage',
1912 params => [ $hostId, '', $licensePoolId ],
1913 id => 1,
1914 };
1915 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1917 # Check Opsi error
1918 my ($res_error, $res_error_str) = &check_opsi_res($res);
1919 if ($res_error){
1920 # Create error message
1921 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1922 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1923 return ( &create_xml_string($out_hash) );
1924 }
1926 # Create function result message
1927 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1929 return ( &create_xml_string($out_hash) );
1930 }
1932 ################################
1933 #
1934 # @brief Unassign all software licenses from a host
1935 # @param hostid Something like client_1.intranet.mydomain.de
1936 #
1937 sub opsi_unassignAllSoftwareLicensesFromHost {
1938 my ($msg, $msg_hash, $session_id) = @_;
1939 my $header = @{$msg_hash->{'header'}}[0];
1940 my $source = @{$msg_hash->{'source'}}[0];
1941 my $target = @{$msg_hash->{'target'}}[0];
1942 my $hostId;
1944 # Check input sanity
1945 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1946 $hostId = @{$msg_hash->{'hostId'}}[0];
1947 } else {
1948 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1949 }
1951 # Unassign all software licenses from a host
1952 my $callobj = {
1953 method => 'deleteAllSoftwareLicenseUsages',
1954 params => [ $hostId ],
1955 id => 1,
1956 };
1957 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1959 # Check Opsi error
1960 my ($res_error, $res_error_str) = &check_opsi_res($res);
1961 if ($res_error){
1962 # Create error message
1963 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1964 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1965 return ( &create_xml_string($out_hash) );
1966 }
1968 # Create function result message
1969 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1971 return ( &create_xml_string($out_hash) );
1972 }
1975 ################################
1976 #
1977 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1978 # and the number of max and remaining installations for a given OPSI product.
1979 # @param productId Identificator of an OPSI product.
1980 #
1981 sub opsi_getLicenseInformationForProduct {
1982 my ($msg, $msg_hash, $session_id) = @_;
1983 my $header = @{$msg_hash->{'header'}}[0];
1984 my $source = @{$msg_hash->{'source'}}[0];
1985 my $productId;
1986 my $out_hash;
1988 # Check input sanity
1989 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1990 $productId = @{$msg_hash->{'productId'}}[0];
1991 } else {
1992 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1993 }
1995 # Fetch infos from Opsi server
1996 my $callobj = {
1997 method => 'getLicensePoolId',
1998 params => [ $productId ],
1999 id => 1,
2000 };
2001 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2003 # Check Opsi error
2004 my ($res_error, $res_error_str) = &check_opsi_res($res);
2005 if ($res_error){
2006 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
2007 }
2009 my $licensePoolId = $res->result;
2011 # Fetch statistic information for given pool ID
2012 $callobj = {
2013 method => 'getLicenseStatistics_hash',
2014 params => [ ],
2015 id => 1,
2016 };
2017 $res = $main::opsi_client->call($main::opsi_url, $callobj);
2019 # Check Opsi error
2020 ($res_error, $res_error_str) = &check_opsi_res($res);
2021 if ($res_error){
2022 # Create error message
2023 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
2024 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
2025 return ( &create_xml_string($out_hash) );
2026 }
2028 # Create function result message
2029 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2030 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
2031 &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
2032 &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
2033 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
2034 &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
2035 map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
2037 return ( &create_xml_string($out_hash) );
2038 }
2041 ################################
2042 #
2043 # @brief
2044 # @param
2045 #
2046 sub opsi_getPool {
2047 my ($msg, $msg_hash, $session_id) = @_;
2048 my $header = @{$msg_hash->{'header'}}[0];
2049 my $source = @{$msg_hash->{'source'}}[0];
2051 # Check input sanity
2052 my $licensePoolId;
2053 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2054 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2055 } else {
2056 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2057 }
2059 # Create hash for the answer
2060 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2062 # Call Opsi
2063 my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2064 if ($err){
2065 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2066 }
2067 # Add data to outgoing hash
2068 &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2069 &add_content2xml_hash($out_hash, "description", $res->{'description'});
2070 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2071 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2074 # Call Opsi two times
2075 my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2076 if ($usages_err){
2077 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
2078 }
2079 my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2080 if ($licenses_err){
2081 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2082 }
2084 # Add data to outgoing hash
2085 # Parse through all software licenses and select those associated to the pool
2086 my $res_hash = { 'hit'=> [] };
2087 foreach my $license ( @$licenses_res) {
2088 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2089 my $found = 0;
2090 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2091 foreach my $lPI ( @licensePoolIds_list) {
2092 if ($lPI eq $licensePoolId) { $found++ }
2093 }
2094 if (not $found ) { next; };
2095 # Found matching licensePoolId
2096 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2097 'licenseKeys' => {},
2098 'expirationDate' => [$license->{'expirationDate'}],
2099 'boundToHost' => [$license->{'boundToHost'}],
2100 'maxInstallations' => [$license->{'maxInstallations'}],
2101 'licenseType' => [$license->{'licenseType'}],
2102 'licenseContractId' => [$license->{'licenseContractId'}],
2103 'licensePoolIds' => [],
2104 'hostIds' => [],
2105 };
2106 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2107 push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2108 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
2109 }
2110 foreach my $usage (@$usages_res) {
2111 # Search for hostIds with matching softwareLicenseId
2112 if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2113 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2114 }
2115 }
2117 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
2118 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
2119 if ($lContract_err){
2120 return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
2121 }
2122 $license_hash->{$license->{'licenseContractId'}} = [];
2123 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
2124 'notificationDate' => [$lContract_res->{notificationDate}],
2125 'notes' => [$lContract_res->{notes}],
2126 'exirationDate' => [$lContract_res->{expirationDate}],
2127 'partner' => [$lContract_res->{partner}],
2128 };
2129 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2131 push( @{$res_hash->{hit}}, $license_hash );
2132 }
2133 $out_hash->{licenses} = [$res_hash];
2135 return ( &create_xml_string($out_hash) );
2136 }
2139 ################################
2140 #
2141 # @brief Removes at first the software license from license pool and than deletes the software license.
2142 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2143 # @param softwareLicenseId
2144 # @param licensePoolId
2145 #
2146 sub opsi_removeLicense {
2147 my ($msg, $msg_hash, $session_id) = @_;
2148 my $header = @{$msg_hash->{'header'}}[0];
2149 my $source = @{$msg_hash->{'source'}}[0];
2151 # Check input sanity
2152 my $softwareLicenseId;
2153 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2154 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2155 } else {
2156 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2157 }
2158 my $licensePoolId;
2159 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2160 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2161 } else {
2162 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2163 }
2165 # Call Opsi
2166 my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2167 if ($err){
2168 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2169 }
2171 # Call Opsi
2172 ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2173 if ($err){
2174 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2175 }
2177 # Create hash for the answer
2178 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2179 return ( &create_xml_string($out_hash) );
2180 }
2183 ################################
2184 #
2185 # @brief
2186 # @param
2187 #
2188 sub opsi_getReservedLicenses {
2189 my ($msg, $msg_hash, $session_id) = @_;
2190 my $header = @{$msg_hash->{'header'}}[0];
2191 my $source = @{$msg_hash->{'source'}}[0];
2193 # Check input sanity
2194 my $hostId;
2195 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2196 $hostId = @{$msg_hash->{'hostId'}}[0];
2197 } else {
2198 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2199 }
2201 # Fetch informations from Opsi server
2202 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2203 if ($license_err){
2204 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2205 }
2208 # Parse result
2209 my $res_hash = { 'hit'=> [] };
2210 foreach my $license ( @$license_res) {
2211 if ($license->{boundToHost} ne $hostId) { next; }
2213 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2214 'maxInstallations' => [$license->{'maxInstallations'}],
2215 'boundToHost' => [$license->{'boundToHost'}],
2216 'expirationDate' => [$license->{'expirationDate'}],
2217 'licenseContractId' => [$license->{'licenseContractId'}],
2218 'licenseType' => [$license->{'licenseType'}],
2219 'licensePoolIds' => [],
2220 };
2222 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2223 # Fetch information for license pools containing a software license which is bound to given host
2224 my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2225 if ($pool_err){
2226 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2227 }
2229 # Add licensePool information to result hash
2230 push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2231 $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2232 map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2233 map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2234 }
2235 push( @{$res_hash->{hit}}, $license_hash );
2236 }
2237 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2238 $out_hash->{licenses} = [$res_hash];
2239 return ( &create_xml_string($out_hash) );
2241 return;
2242 }
2244 ################################
2245 #
2246 # @brief
2247 # @param
2248 #
2249 sub opsi_boundHostToLicense {
2250 my ($msg, $msg_hash, $session_id) = @_;
2251 my $header = @{$msg_hash->{'header'}}[0];
2252 my $source = @{$msg_hash->{'source'}}[0];
2254 # Check input sanity
2255 my $hostId;
2256 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2257 $hostId = @{$msg_hash->{'hostId'}}[0];
2258 } else {
2259 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2260 }
2261 my $softwareLicenseId;
2262 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2263 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2264 } else {
2265 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2266 }
2268 # Fetch informations from Opsi server
2269 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2270 if ($license_err){
2271 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2272 }
2274 # Memorize parameter for given softwareLicenseId
2275 my $licenseContractId;
2276 my $licenseType;
2277 my $maxInstallations;
2278 my $boundToHost;
2279 my $expirationDate = "";
2280 my $found;
2281 foreach my $license (@$license_res) {
2282 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2283 $licenseContractId = $license->{licenseContractId};
2284 $licenseType = $license->{licenseType};
2285 $maxInstallations = $license->{maxInstallations};
2286 $expirationDate = $license->{expirationDate};
2287 $found++;
2288 }
2290 if (not $found) {
2291 return ( &_give_feedback($msg, $msg_hash, $session_id, "no softwarelicenseId found with name '".$softwareLicenseId."'") );
2292 }
2294 # Set boundToHost option for a given software license
2295 my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId,
2296 'licenseContractId' => $licenseContractId,
2297 'licenseType' => $licenseType,
2298 'maxInstallations' => $maxInstallations,
2299 'boundToHost' => $hostId,
2300 'expirationDate' => $expirationDate);
2301 if ($bound_err) {
2302 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2303 }
2305 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2306 return ( &create_xml_string($out_hash) );
2307 }
2309 ################################
2310 #
2311 # @brief
2312 # @param
2313 #
2314 sub opsi_unboundHostFromLicense {
2315 # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2316 # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2317 # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2318 my ($msg, $msg_hash, $session_id) = @_;
2319 my $header = @{$msg_hash->{'header'}}[0];
2320 my $source = @{$msg_hash->{'source'}}[0];
2322 # Check input sanity
2323 my $softwareLicenseId;
2324 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2325 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2326 } else {
2327 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2328 }
2330 # Memorize parameter witch are required for this procedure
2331 my $licenseContractId;
2332 my $licenseType;
2333 my $maxInstallations;
2334 my $expirationDate;
2335 my $partner;
2336 my $conclusionDate;
2337 my $notificationDate;
2338 my $notes;
2339 my $licensePoolId;
2340 my $licenseKey;
2342 # Fetch license informations from Opsi server
2343 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2344 if ($license_err){
2345 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2346 }
2347 my $found = 0;
2348 foreach my $license (@$license_res) {
2349 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2350 $licenseContractId = $license->{licenseContractId};
2351 $licenseType = $license->{licenseType};
2352 $maxInstallations = $license->{maxInstallations};
2353 $expirationDate = $license->{expirationDate};
2354 $licensePoolId = @{$license->{licensePoolIds}}[0];
2355 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2356 $found++;
2357 }
2359 # Fetch contract informations from Opsi server
2360 my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2361 if ($contract_err){
2362 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2363 }
2364 $partner = $contract_res->{partner};
2365 $conclusionDate = $contract_res->{conclusionDate};
2366 $notificationDate = $contract_res->{notificationDate};
2367 $expirationDate = $contract_res->{expirationDate};
2368 $notes = $contract_res->{notes};
2370 # Delete software license
2371 my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2372 if ($err) {
2373 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2374 }
2376 # Recreate software license without boundToHost
2377 ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate,
2378 'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2379 if ($err) {
2380 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2381 }
2382 ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType,
2383 'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate );
2384 if ($err) {
2385 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2386 }
2387 ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2388 if ($err) {
2389 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2390 }
2392 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2393 return ( &create_xml_string($out_hash) );
2394 }
2396 sub opsi_test {
2397 my ($msg, $msg_hash, $session_id) = @_;
2398 my $header = @{$msg_hash->{'header'}}[0];
2399 my $source = @{$msg_hash->{'source'}}[0];
2400 my $pram1 = @{$msg_hash->{'productId'}}[0];
2402 print STDERR Dumper $pram1;
2404 # Fetch infos from Opsi server
2405 my $callobj = {
2406 method => 'getLicensePoolId',
2407 params => [ $pram1 ],
2408 id => 1,
2409 };
2410 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2412 print STDERR Dumper $res;
2413 return ();
2414 }
2416 sub _giveErrorFeedback {
2417 my ($msg_hash, $err_string, $session_id) = @_;
2418 &main::daemon_log("$session_id ERROR: $err_string", 1);
2419 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2420 return ( &create_xml_string($out_hash) );
2421 }
2424 sub _getLicensePool_hash {
2425 my %arg = (
2426 'licensePoolId' => undef,
2427 @_,
2428 );
2430 if (not defined $arg{licensePoolId} ) {
2431 return ("function requires licensePoolId as parameter", 1);
2432 }
2434 # Fetch pool infos from Opsi server
2435 my $callobj = {
2436 method => 'getLicensePool_hash',
2437 params => [ $arg{licensePoolId} ],
2438 id => 1,
2439 };
2440 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2442 # Check Opsi error
2443 my ($res_error, $res_error_str) = &check_opsi_res($res);
2444 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2446 return ($res->result, 0);
2447 }
2449 sub _getSoftwareLicenses_listOfHashes {
2450 # Fetch licenses associated to the given pool
2451 my $callobj = {
2452 method => 'getSoftwareLicenses_listOfHashes',
2453 params => [ ],
2454 id => 1,
2455 };
2456 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2458 # Check Opsi error
2459 my ($res_error, $res_error_str) = &check_opsi_res($res);
2460 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2462 return ($res->result, 0);
2463 }
2465 sub _getSoftwareLicenseUsages_listOfHashes {
2466 my %arg = (
2467 'hostId' => "",
2468 'licensePoolId' => "",
2469 @_,
2470 );
2472 # Fetch pool infos from Opsi server
2473 my $callobj = {
2474 method => 'getSoftwareLicenseUsages_listOfHashes',
2475 params => [ $arg{hostId}, $arg{licensePoolId} ],
2476 id => 1,
2477 };
2478 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2480 # Check Opsi error
2481 my ($res_error, $res_error_str) = &check_opsi_res($res);
2482 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2484 return ($res->result, 0);
2485 }
2487 sub _removeSoftwareLicenseFromLicensePool {
2488 my %arg = (
2489 'softwareLicenseId' => undef,
2490 'licensePoolId' => undef,
2491 @_,
2492 );
2494 if (not defined $arg{softwareLicenseId} ) {
2495 return ("function requires softwareLicenseId as parameter", 1);
2496 }
2497 if (not defined $arg{licensePoolId} ) {
2498 return ("function requires licensePoolId as parameter", 1);
2499 }
2501 # Remove software license from license pool
2502 my $callobj = {
2503 method => 'removeSoftwareLicenseFromLicensePool',
2504 params => [ $arg{softwareLicenseId}, $arg{licensePoolId} ],
2505 id => 1,
2506 };
2507 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2509 # Check Opsi error
2510 my ($res_error, $res_error_str) = &check_opsi_res($res);
2511 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2513 return ($res->result, 0);
2514 }
2516 sub _deleteSoftwareLicense {
2517 my %arg = (
2518 'softwareLicenseId' => undef,
2519 'removeFromPools' => "false",
2520 @_,
2521 );
2523 if (not defined $arg{softwareLicenseId} ) {
2524 return ("function requires softwareLicenseId as parameter", 1);
2525 }
2526 my $removeFromPools = "";
2527 if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) {
2528 $removeFromPools = "removeFromPools";
2529 }
2531 # Fetch
2532 my $callobj = {
2533 method => 'deleteSoftwareLicense',
2534 params => [ $arg{softwareLicenseId}, $removeFromPools ],
2535 id => 1,
2536 };
2537 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2539 # Check Opsi error
2540 my ($res_error, $res_error_str) = &check_opsi_res($res);
2541 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2543 return ($res->result, 0);
2544 }
2546 sub _getLicensePoolId {
2547 my %arg = (
2548 'productId' => undef,
2549 @_,
2550 );
2552 if (not defined $arg{productId} ) {
2553 return ("function requires productId as parameter", 1);
2554 }
2556 my $callobj = {
2557 method => 'getLicensePoolId',
2558 params => [ $arg{productId} ],
2559 id => 1,
2560 };
2561 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2563 # Check Opsi error
2564 my ($res_error, $res_error_str) = &check_opsi_res($res);
2565 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2567 return ($res->result, 0);
2568 }
2570 sub _getLicenseContract_hash {
2571 my %arg = (
2572 'licenseContractId' => undef,
2573 @_,
2574 );
2576 if (not defined $arg{licenseContractId} ) {
2577 return ("function requires licenseContractId as parameter", 1);
2578 }
2580 my $callobj = {
2581 method => 'getLicenseContract_hash',
2582 params => [ $arg{licenseContractId} ],
2583 id => 1,
2584 };
2585 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2587 # Check Opsi error
2588 my ($res_error, $res_error_str) = &check_opsi_res($res);
2589 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2591 return ($res->result, 0);
2592 }
2594 sub _createLicenseContract {
2595 my %arg = (
2596 'licenseContractId' => undef,
2597 'partner' => undef,
2598 'conclusionDate' => undef,
2599 'notificationDate' => undef,
2600 'expirationDate' => undef,
2601 'notes' => undef,
2602 @_,
2603 );
2605 # Create license contract at Opsi server
2606 my $callobj = {
2607 method => 'createLicenseContract',
2608 params => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2609 id => 1,
2610 };
2611 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2613 # Check Opsi error
2614 my ($res_error, $res_error_str) = &check_opsi_res($res);
2615 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2617 return ($res->result, 0);
2618 }
2620 sub _createSoftwareLicense {
2621 my %arg = (
2622 'softwareLicenseId' => undef,
2623 'licenseContractId' => undef,
2624 'licenseType' => undef,
2625 'maxInstallations' => undef,
2626 'boundToHost' => undef,
2627 'expirationDate' => undef,
2628 @_,
2629 );
2631 my $callobj = {
2632 method => 'createSoftwareLicense',
2633 params => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2634 id => 1,
2635 };
2636 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2638 # Check Opsi error
2639 my ($res_error, $res_error_str) = &check_opsi_res($res);
2640 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2642 return ($res->result, 0);
2643 }
2645 sub _addSoftwareLicenseToLicensePool {
2646 my %arg = (
2647 'softwareLicenseId' => undef,
2648 'licensePoolId' => undef,
2649 'licenseKey' => undef,
2650 @_,
2651 );
2653 if (not defined $arg{softwareLicenseId} ) {
2654 return ("function requires softwareLicenseId as parameter", 1);
2655 }
2656 if (not defined $arg{licensePoolId} ) {
2657 return ("function requires licensePoolId as parameter", 1);
2658 }
2660 # Add software license to license pool
2661 my $callobj = {
2662 method => 'addSoftwareLicenseToLicensePool',
2663 params => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ],
2664 id => 1,
2665 };
2666 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2668 # Check Opsi error
2669 my ($res_error, $res_error_str) = &check_opsi_res($res);
2670 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2672 return ($res->result, 0);
2673 }
2675 1;