1 ## @file
2 # @details A GOsa-SI-server event module containing all functions for message handling.
3 # @brief Implementation of an event module for GOsa-SI-server.
6 package opsi_com;
7 use Exporter;
8 @ISA = qw(Exporter);
9 my @events = (
10 "get_events",
11 "opsi_install_client",
12 "opsi_get_netboot_products",
13 "opsi_get_local_products",
14 "opsi_get_client_hardware",
15 "opsi_get_client_software",
16 "opsi_get_product_properties",
17 "opsi_set_product_properties",
18 "opsi_list_clients",
19 "opsi_del_client",
20 "opsi_add_client",
21 "opsi_modify_client",
22 "opsi_add_product_to_client",
23 "opsi_del_product_from_client",
24 "opsi_createLicensePool",
25 "opsi_deleteLicensePool",
26 "opsi_createLicense",
27 "opsi_assignSoftwareLicenseToHost",
28 "opsi_unassignSoftwareLicenseFromHost",
29 "opsi_unassignAllSoftwareLicensesFromHost",
30 "opsi_getSoftwareLicense_hash",
31 "opsi_getLicensePool_hash",
32 "opsi_getSoftwareLicenseUsages",
33 "opsi_getSoftwareLicenseUsagesForProductId",
34 "opsi_getLicensePools_listOfHashes",
35 "opsi_getLicenseInformationForProduct",
36 "opsi_getPool",
37 "opsi_getAllSoftwareLicenses",
38 "opsi_removeLicense",
39 "opsi_getReservedLicenses",
40 "opsi_boundHostToLicense",
41 "opsi_unboundHostFromLicense",
42 "opsi_test",
43 );
44 @EXPORT = @events;
46 use strict;
47 use warnings;
48 use GOSA::GosaSupportDaemon;
49 use Data::Dumper;
50 use XML::Quote qw(:all);
52 BEGIN {}
54 END {}
56 # ----------------------------------------------------------------------------
57 # D E C L A R A T I O N S
58 # ----------------------------------------------------------------------------
60 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
64 # ----------------------------------------------------------------------------
65 # S U B R O U T I N E S
66 # ----------------------------------------------------------------------------
69 ################################
70 #
71 # @brief A function returning a list of functions which are exported by importing the module.
72 # @return List of all provided functions
73 #
74 sub get_events {
75 return \@events;
76 }
78 ################################
79 #
80 # @brief Checks if there is a specified tag and if the the tag has a content.
81 # @return 0|1
82 #
83 sub _check_xml_tag_is_ok {
84 my ($msg_hash,$tag) = @_;
85 if (not defined $msg_hash->{$tag}) {
86 $_ = "message contains no tag '$tag'";
87 return 0;
88 }
89 if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
90 $_ = "message tag '$tag' has no content";
91 return 0;
92 }
93 return 1;
94 }
96 ################################
97 #
98 # @brief Writes the log line and returns the error message for GOsa.
99 #
100 sub _giveErrorFeedback {
101 my ($msg_hash, $err_string, $session_id) = @_;
102 &main::daemon_log("$session_id ERROR: $err_string", 1);
103 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
104 if (exists $msg_hash->{forward_to_gosa}) {
105 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
106 }
107 return ( &create_xml_string($out_hash) );
108 }
111 ## @method opsi_add_product_to_client
112 # Adds an Opsi product to an Opsi client.
113 # @param msg - STRING - xml message with tags hostId and productId
114 # @param msg_hash - HASHREF - message information parsed into a hash
115 # @param session_id - INTEGER - POE session id of the processing of this message
116 # @return out_msg - STRING - feedback to GOsa in success and error case
117 sub opsi_add_product_to_client {
118 my ($msg, $msg_hash, $session_id) = @_;
119 my $header = @{$msg_hash->{'header'}}[0];
120 my $source = @{$msg_hash->{'source'}}[0];
121 my $target = @{$msg_hash->{'target'}}[0];
122 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
124 # Build return message
125 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
126 if (defined $forward_to_gosa) {
127 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
128 }
130 # Sanity check of needed parameter
131 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
132 return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
133 }
134 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
135 return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
136 }
138 # Get hostId
139 my $hostId = @{$msg_hash->{'hostId'}}[0];
140 &add_content2xml_hash($out_hash, "hostId", $hostId);
142 # Get productID
143 my $productId = @{$msg_hash->{'productId'}}[0];
144 &add_content2xml_hash($out_hash, "productId", $productId);
146 # Do an action request for all these -> "setup".
147 my $callobj = {
148 method => 'setProductActionRequest',
149 params => [ $productId, $hostId, "setup" ],
150 id => 1, };
152 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
153 if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
155 # return message
156 return ( &create_xml_string($out_hash) );
157 }
159 ## @method opsi_del_product_from_client
160 # Deletes an Opsi-product from an Opsi-client.
161 # @param msg - STRING - xml message with tags hostId and productId
162 # @param msg_hash - HASHREF - message information parsed into a hash
163 # @param session_id - INTEGER - POE session id of the processing of this message
164 # @return out_msg - STRING - feedback to GOsa in success and error case
165 sub opsi_del_product_from_client {
166 my ($msg, $msg_hash, $session_id) = @_;
167 my $header = @{$msg_hash->{'header'}}[0];
168 my $source = @{$msg_hash->{'source'}}[0];
169 my $target = @{$msg_hash->{'target'}}[0];
170 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
171 my ($hostId, $productId);
172 my $error = 0;
173 my ($sres, $sres_err, $sres_err_string);
175 # Build return message
176 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
177 if (defined $forward_to_gosa) {
178 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
179 }
181 # Sanity check of needed parameter
182 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
183 $error++;
184 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
185 &add_content2xml_hash($out_hash, "error", "hostId");
186 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
188 }
189 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
190 $error++;
191 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
192 &add_content2xml_hash($out_hash, "error", "productId");
193 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
194 }
196 # All parameter available
197 if (not $error) {
198 # Get hostId
199 $hostId = @{$msg_hash->{'hostId'}}[0];
200 &add_content2xml_hash($out_hash, "hostId", $hostId);
202 # Get productID
203 $productId = @{$msg_hash->{'productId'}}[0];
204 &add_content2xml_hash($out_hash, "productId", $productId);
207 # : check the results for more than one entry which is currently installed
208 #$callobj = {
209 # method => 'getProductDependencies_listOfHashes',
210 # params => [ $productId ],
211 # id => 1, };
212 #
213 #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
214 #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
215 #if ($sres_err){
216 # &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
217 # &add_content2xml_hash($out_hash, "error", $sres_err_string);
218 # return ( &create_xml_string($out_hash) );
219 #}
222 # Check to get product action list
223 my $callobj = {
224 method => 'getPossibleProductActions_list',
225 params => [ $productId ],
226 id => 1, };
227 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
228 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
229 if ($sres_err){
230 &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
231 &add_content2xml_hash($out_hash, "error", $sres_err_string);
232 $error++;
233 }
234 }
236 # Check action uninstall of product
237 if (not $error) {
238 my $uninst_possible= 0;
239 foreach my $r (@{$sres->result}) {
240 if ($r eq 'uninstall') {
241 $uninst_possible= 1;
242 }
243 }
244 if (!$uninst_possible){
245 &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
246 &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
247 $error++;
248 }
249 }
251 # Set product state to "none"
252 # Do an action request for all these -> "setup".
253 if (not $error) {
254 my $callobj = {
255 method => 'setProductActionRequest',
256 params => [ $productId, $hostId, "none" ],
257 id => 1,
258 };
259 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
260 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
261 if ($sres_err){
262 &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
263 &add_content2xml_hash($out_hash, "error", $sres_err_string);
264 }
265 }
267 # Return message
268 return ( &create_xml_string($out_hash) );
269 }
271 ## @method opsi_add_client
272 # Adds an Opsi client to Opsi.
273 # @param msg - STRING - xml message with tags hostId and macaddress
274 # @param msg_hash - HASHREF - message information parsed into a hash
275 # @param session_id - INTEGER - POE session id of the processing of this message
276 # @return out_msg - STRING - feedback to GOsa in success and error case
277 sub opsi_add_client {
278 my ($msg, $msg_hash, $session_id) = @_;
279 my $header = @{$msg_hash->{'header'}}[0];
280 my $source = @{$msg_hash->{'source'}}[0];
281 my $target = @{$msg_hash->{'target'}}[0];
282 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
283 my ($hostId, $mac);
284 my $error = 0;
285 my ($sres, $sres_err, $sres_err_string);
287 # Build return message with twisted target and source
288 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
289 if (defined $forward_to_gosa) {
290 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
291 }
293 # Sanity check of needed parameter
294 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
295 $error++;
296 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
297 &add_content2xml_hash($out_hash, "error", "hostId");
298 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
299 }
300 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH')) {
301 $error++;
302 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
303 &add_content2xml_hash($out_hash, "error", "macaddress");
304 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
305 }
307 if (not $error) {
308 # Get hostId
309 $hostId = @{$msg_hash->{'hostId'}}[0];
310 &add_content2xml_hash($out_hash, "hostId", $hostId);
312 # Get macaddress
313 $mac = @{$msg_hash->{'macaddress'}}[0];
314 &add_content2xml_hash($out_hash, "macaddress", $mac);
316 my $name= $hostId;
317 $name=~ s/^([^.]+).*$/$1/;
318 my $domain= $hostId;
319 $domain=~ s/^[^.]+\.(.*)$/$1/;
320 my ($description, $notes, $ip);
322 if (defined @{$msg_hash->{'description'}}[0]){
323 $description = @{$msg_hash->{'description'}}[0];
324 }
325 if (defined @{$msg_hash->{'notes'}}[0]){
326 $notes = @{$msg_hash->{'notes'}}[0];
327 }
328 if (defined @{$msg_hash->{'ip'}}[0]){
329 $ip = @{$msg_hash->{'ip'}}[0];
330 }
332 my $callobj;
333 $callobj = {
334 method => 'createClient',
335 params => [ $name, $domain, $description, $notes, $ip, $mac ],
336 id => 1,
337 };
339 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
340 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
341 if ($sres_err){
342 &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
343 &add_content2xml_hash($out_hash, "error", $sres_err_string);
344 } else {
345 &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5);
346 }
347 }
349 # Return message
350 return ( &create_xml_string($out_hash) );
351 }
353 ## @method opsi_modify_client
354 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
355 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
356 # @param msg_hash - HASHREF - message information parsed into a hash
357 # @param session_id - INTEGER - POE session id of the processing of this message
358 # @return out_msg - STRING - feedback to GOsa in success and error case
359 sub opsi_modify_client {
360 my ($msg, $msg_hash, $session_id) = @_;
361 my $header = @{$msg_hash->{'header'}}[0];
362 my $source = @{$msg_hash->{'source'}}[0];
363 my $target = @{$msg_hash->{'target'}}[0];
364 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
365 my $hostId;
366 my $error = 0;
367 my ($sres, $sres_err, $sres_err_string);
369 # Build return message with twisted target and source
370 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
371 if (defined $forward_to_gosa) {
372 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
373 }
375 # Sanity check of needed parameter
376 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
377 $error++;
378 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
379 &add_content2xml_hash($out_hash, "error", "hostId");
380 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
381 }
383 if (not $error) {
384 # Get hostId
385 $hostId = @{$msg_hash->{'hostId'}}[0];
386 &add_content2xml_hash($out_hash, "hostId", $hostId);
387 my $name= $hostId;
388 $name=~ s/^([^.]+).*$/$1/;
389 my $domain= $hostId;
390 $domain=~ s/^[^.]+(.*)$/$1/;
392 # Modify description, notes or mac if defined
393 my ($description, $notes, $mac);
394 my $callobj;
395 if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
396 $description = @{$msg_hash->{'description'}}[0];
397 $callobj = {
398 method => 'setHostDescription',
399 params => [ $hostId, $description ],
400 id => 1,
401 };
402 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
403 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
404 if ($sres_err){
405 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
406 &add_content2xml_hash($out_hash, "error", $sres_err_string);
407 }
408 }
409 if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
410 $notes = @{$msg_hash->{'notes'}}[0];
411 $callobj = {
412 method => 'setHostNotes',
413 params => [ $hostId, $notes ],
414 id => 1,
415 };
416 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
417 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
418 if ($sres_err){
419 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
420 &add_content2xml_hash($out_hash, "error", $sres_err_string);
421 }
422 }
423 if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
424 $mac = @{$msg_hash->{'mac'}}[0];
425 $callobj = {
426 method => 'setMacAddress',
427 params => [ $hostId, $mac ],
428 id => 1,
429 };
430 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
431 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
432 if ($sres_err){
433 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
434 &add_content2xml_hash($out_hash, "error", $sres_err_string);
435 }
436 }
437 }
439 # Return message
440 return ( &create_xml_string($out_hash) );
441 }
444 ## @method opsi_get_netboot_products
445 # Get netboot products for specific host.
446 # @param msg - STRING - xml message with tag hostId
447 # @param msg_hash - HASHREF - message information parsed into a hash
448 # @param session_id - INTEGER - POE session id of the processing of this message
449 # @return out_msg - STRING - feedback to GOsa in success and error case
450 sub opsi_get_netboot_products {
451 my ($msg, $msg_hash, $session_id) = @_;
452 my $header = @{$msg_hash->{'header'}}[0];
453 my $source = @{$msg_hash->{'source'}}[0];
454 my $target = @{$msg_hash->{'target'}}[0];
455 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
456 my $hostId;
457 my $xml_msg;
459 # Build return message with twisted target and source
460 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
461 if (defined $forward_to_gosa) {
462 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
463 }
465 # Get hostId if defined
466 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
467 $hostId = @{$msg_hash->{'hostId'}}[0];
468 &add_content2xml_hash($out_hash, "hostId", $hostId);
469 }
471 &add_content2xml_hash($out_hash, "xxx", "");
472 $xml_msg = &create_xml_string($out_hash);
473 # For hosts, only return the products that are or get installed
474 my $callobj;
475 $callobj = {
476 method => 'getNetBootProductIds_list',
477 params => [ ],
478 id => 1,
479 };
480 &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
481 &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
482 &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
483 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
484 &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
485 my %r = ();
486 for (@{$res->result}) { $r{$_} = 1 }
488 if (not &check_opsi_res($res)){
490 if (defined $hostId){
492 $callobj = {
493 method => 'getProductStates_hash',
494 params => [ $hostId ],
495 id => 1,
496 };
498 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
499 if (not &check_opsi_res($hres)){
500 my $htmp= $hres->result->{$hostId};
502 # check state != not_installed or action == setup -> load and add
503 foreach my $product (@{$htmp}){
505 if (!defined ($r{$product->{'productId'}})){
506 next;
507 }
509 # Now we've a couple of hashes...
510 if ($product->{'installationStatus'} ne "not_installed" or
511 $product->{'actionRequest'} eq "setup"){
512 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
514 $callobj = {
515 method => 'getProduct_hash',
516 params => [ $product->{'productId'} ],
517 id => 1,
518 };
520 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
521 if (not &check_opsi_res($sres)){
522 my $tres= $sres->result;
524 my $name= xml_quote($tres->{'name'});
525 my $r= $product->{'productId'};
526 my $description= xml_quote($tres->{'description'});
527 $name=~ s/\//\\\//;
528 $description=~ s/\//\\\//;
529 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
530 }
531 }
532 }
534 }
536 } else {
537 foreach my $r (@{$res->result}) {
538 $callobj = {
539 method => 'getProduct_hash',
540 params => [ $r ],
541 id => 1,
542 };
544 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
545 if (not &check_opsi_res($sres)){
546 my $tres= $sres->result;
548 my $name= xml_quote($tres->{'name'});
549 my $description= xml_quote($tres->{'description'});
550 $name=~ s/\//\\\//;
551 $description=~ s/\//\\\//;
552 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
553 }
554 }
556 }
557 }
558 $xml_msg=~ s/<xxx><\/xxx>//;
560 # Return message
561 return ( $xml_msg );
562 }
565 ## @method opsi_get_product_properties
566 # Get product properties for a product and a specific host or gobally for a product.
567 # @param msg - STRING - xml message with tags productId and optional hostId
568 # @param msg_hash - HASHREF - message information parsed into a hash
569 # @param session_id - INTEGER - POE session id of the processing of this message
570 # @return out_msg - STRING - feedback to GOsa in success and error case
571 sub opsi_get_product_properties {
572 my ($msg, $msg_hash, $session_id) = @_;
573 my $header = @{$msg_hash->{'header'}}[0];
574 my $source = @{$msg_hash->{'source'}}[0];
575 my $target = @{$msg_hash->{'target'}}[0];
576 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
577 my ($hostId, $productId);
578 my $xml_msg;
580 # Build return message with twisted target and source
581 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
582 if (defined $forward_to_gosa) {
583 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
584 }
586 # Sanity check of needed parameter
587 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
588 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
589 &add_content2xml_hash($out_hash, "error", "productId");
590 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
592 # Return message
593 return ( &create_xml_string($out_hash) );
594 }
596 # Get productid
597 $productId = @{$msg_hash->{'productId'}}[0];
598 &add_content2xml_hash($out_hash, "producId", "$productId");
600 # Get hostId if defined
601 if (defined @{$msg_hash->{'hostId'}}[0]){
602 $hostId = @{$msg_hash->{'hostId'}}[0];
603 &add_content2xml_hash($out_hash, "hostId", $hostId);
604 }
606 # Load actions
607 my $callobj = {
608 method => 'getPossibleProductActions_list',
609 params => [ $productId ],
610 id => 1,
611 };
612 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
613 if (not &check_opsi_res($res)){
614 foreach my $action (@{$res->result}){
615 &add_content2xml_hash($out_hash, "action", $action);
616 }
617 }
619 # Add place holder
620 &add_content2xml_hash($out_hash, "xxx", "");
622 # Move to XML string
623 $xml_msg= &create_xml_string($out_hash);
625 # JSON Query
626 if (defined $hostId){
627 $callobj = {
628 method => 'getProductProperties_hash',
629 params => [ $productId, $hostId ],
630 id => 1,
631 };
632 } else {
633 $callobj = {
634 method => 'getProductProperties_hash',
635 params => [ $productId ],
636 id => 1,
637 };
638 }
639 $res = $main::opsi_client->call($main::opsi_url, $callobj);
641 # JSON Query 2
642 $callobj = {
643 method => 'getProductPropertyDefinitions_listOfHashes',
644 params => [ $productId ],
645 id => 1,
646 };
648 # Assemble options
649 my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
650 my $values = {};
651 my $descriptions = {};
652 if (not &check_opsi_res($res2)){
653 my $r= $res2->result;
655 foreach my $entr (@$r){
656 # Unroll values
657 my $cnv;
658 if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
659 foreach my $v (@{$entr->{'values'}}){
660 $cnv.= "<value>$v</value>";
661 }
662 } else {
663 $cnv= $entr->{'values'};
664 }
665 $values->{$entr->{'name'}}= $cnv;
666 $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
667 }
668 }
670 if (not &check_opsi_res($res)){
671 my $r= $res->result;
672 foreach my $key (keys %{$r}) {
673 my $item= "\n<item>";
674 my $value= $r->{$key};
675 my $dsc= "";
676 my $vals= "";
677 if (defined $descriptions->{$key}){
678 $dsc= $descriptions->{$key};
679 }
680 if (defined $values->{$key}){
681 $vals= $values->{$key};
682 }
683 $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
684 $item.= "</item>";
685 $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
686 }
687 }
689 $xml_msg=~ s/<xxx><\/xxx>//;
691 # Return message
692 return ( $xml_msg );
693 }
696 ## @method opsi_set_product_properties
697 # 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.
698 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
699 # @param msg_hash - HASHREF - message information parsed into a hash
700 # @param session_id - INTEGER - POE session id of the processing of this message
701 # @return out_msg - STRING - feedback to GOsa in success and error case
702 sub opsi_set_product_properties {
703 my ($msg, $msg_hash, $session_id) = @_;
704 my $header = @{$msg_hash->{'header'}}[0];
705 my $source = @{$msg_hash->{'source'}}[0];
706 my $target = @{$msg_hash->{'target'}}[0];
707 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
708 my ($productId, $hostId);
710 # Build return message with twisted target and source
711 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
712 if (defined $forward_to_gosa) {
713 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
714 }
716 # Sanity check of needed parameter
717 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
718 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
719 &add_content2xml_hash($out_hash, "error", "productId");
720 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
721 return ( &create_xml_string($out_hash) );
722 }
723 if (not exists $msg_hash->{'item'}) {
724 &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
725 &add_content2xml_hash($out_hash, "error", "item");
726 &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1);
727 return ( &create_xml_string($out_hash) );
728 } else {
729 if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
730 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
731 &add_content2xml_hash($out_hash, "error", "name");
732 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1);
733 return ( &create_xml_string($out_hash) );
734 }
735 if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
736 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
737 &add_content2xml_hash($out_hash, "error", "value");
738 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1);
739 return ( &create_xml_string($out_hash) );
740 }
741 }
742 # if no hostId is given, set_product_properties will act on globally
743 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1)) {
744 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
745 &add_content2xml_hash($out_hash, "error", "hostId");
746 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
747 return ( &create_xml_string($out_hash) );
748 }
751 # Get productId
752 $productId = @{$msg_hash->{'productId'}}[0];
753 &add_content2xml_hash($out_hash, "productId", $productId);
755 # Get hostId if defined
756 if (exists $msg_hash->{'hostId'}){
757 $hostId = @{$msg_hash->{'hostId'}}[0];
758 &add_content2xml_hash($out_hash, "hostId", $hostId);
759 }
761 # Set product states if requested
762 if (defined @{$msg_hash->{'action'}}[0]){
763 &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
764 }
765 if (defined @{$msg_hash->{'state'}}[0]){
766 &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
767 }
769 # Find properties
770 foreach my $item (@{$msg_hash->{'item'}}){
771 # JSON Query
772 my $callobj;
774 if (defined $hostId){
775 $callobj = {
776 method => 'setProductProperty',
777 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
778 id => 1,
779 };
780 } else {
781 $callobj = {
782 method => 'setProductProperty',
783 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
784 id => 1,
785 };
786 }
788 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
789 my ($res_err, $res_err_string) = &check_opsi_res($res);
791 if ($res_err){
792 &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
793 &add_content2xml_hash($out_hash, "error", $res_err_string);
794 }
795 }
798 # Return message
799 return ( &create_xml_string($out_hash) );
800 }
803 ## @method opsi_get_client_hardware
804 # Reports client hardware inventory.
805 # @param msg - STRING - xml message with tag hostId
806 # @param msg_hash - HASHREF - message information parsed into a hash
807 # @param session_id - INTEGER - POE session id of the processing of this message
808 # @return out_msg - STRING - feedback to GOsa in success and error case
809 sub opsi_get_client_hardware {
810 my ($msg, $msg_hash, $session_id) = @_;
811 my $header = @{$msg_hash->{'header'}}[0];
812 my $source = @{$msg_hash->{'source'}}[0];
813 my $target = @{$msg_hash->{'target'}}[0];
814 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
815 my $hostId;
816 my $error = 0;
817 my $xml_msg;
819 # Build return message with twisted target and source
820 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
821 if (defined $forward_to_gosa) {
822 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
823 }
825 # Sanity check of needed parameter
826 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
827 $error++;
828 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
829 &add_content2xml_hash($out_hash, "error", "hostId");
830 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
831 }
833 if (not $error) {
835 # Get hostId
836 $hostId = @{$msg_hash->{'hostId'}}[0];
837 &add_content2xml_hash($out_hash, "hostId", "$hostId");
838 &add_content2xml_hash($out_hash, "xxx", "");
839 }
841 # Move to XML string
842 $xml_msg= &create_xml_string($out_hash);
844 if (not $error) {
846 # JSON Query
847 my $callobj = {
848 method => 'getHardwareInformation_hash',
849 params => [ $hostId ],
850 id => 1,
851 };
853 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
854 if (not &check_opsi_res($res)){
855 my $result= $res->result;
856 if (ref $result eq "HASH") {
857 foreach my $r (keys %{$result}){
858 my $item= "\n<item><id>".xml_quote($r)."</id>";
859 my $value= $result->{$r};
860 foreach my $sres (@{$value}){
862 foreach my $dres (keys %{$sres}){
863 if (defined $sres->{$dres}){
864 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
865 }
866 }
868 }
869 $item.= "</item>";
870 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
872 }
873 }
874 }
876 $xml_msg=~ s/<xxx><\/xxx>//;
878 }
880 # Return message
881 return ( $xml_msg );
882 }
885 ## @method opsi_list_clients
886 # Reports all Opsi clients.
887 # @param msg - STRING - xml message
888 # @param msg_hash - HASHREF - message information parsed into a hash
889 # @param session_id - INTEGER - POE session id of the processing of this message
890 # @return out_msg - STRING - feedback to GOsa in success and error case
891 sub opsi_list_clients {
892 my ($msg, $msg_hash, $session_id) = @_;
893 my $header = @{$msg_hash->{'header'}}[0];
894 my $source = @{$msg_hash->{'source'}}[0];
895 my $target = @{$msg_hash->{'target'}}[0];
896 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
898 # Build return message with twisted target and source
899 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
900 if (defined $forward_to_gosa) {
901 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
902 }
903 &add_content2xml_hash($out_hash, "xxx", "");
905 # Move to XML string
906 my $xml_msg= &create_xml_string($out_hash);
908 # JSON Query
909 my $callobj = {
910 method => 'getClients_listOfHashes',
911 params => [ ],
912 id => 1,
913 };
914 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
915 if (not &check_opsi_res($res)){
916 foreach my $host (@{$res->result}){
917 my $item= "\n<item><name>".$host->{'hostId'}."</name>";
918 if (defined($host->{'description'})){
919 $item.= "<description>".xml_quote($host->{'description'})."</description>";
920 }
921 if (defined($host->{'notes'})){
922 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
923 }
924 if (defined($host->{'lastSeen'})){
925 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
926 }
928 $callobj = {
929 method => 'getIpAddress',
930 params => [ $host->{'hostId'} ],
931 id => 1,
932 };
933 my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
934 if ( not &check_opsi_res($sres)){
935 $item.= "<ip>".xml_quote($sres->result)."</ip>";
936 }
938 $callobj = {
939 method => 'getMacAddress',
940 params => [ $host->{'hostId'} ],
941 id => 1,
942 };
943 $sres= $main::opsi_client->call($main::opsi_url, $callobj);
944 if ( not &check_opsi_res($sres)){
945 $item.= "<mac>".xml_quote($sres->result)."</mac>";
946 }
947 $item.= "</item>";
948 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
949 }
950 }
952 $xml_msg=~ s/<xxx><\/xxx>//;
953 return ( $xml_msg );
954 }
958 ## @method opsi_get_client_software
959 # Reports client software inventory.
960 # @param msg - STRING - xml message with tag hostId
961 # @param msg_hash - HASHREF - message information parsed into a hash
962 # @param session_id - INTEGER - POE session id of the processing of this message
963 # @return out_msg - STRING - feedback to GOsa in success and error case
964 sub opsi_get_client_software {
965 my ($msg, $msg_hash, $session_id) = @_;
966 my $header = @{$msg_hash->{'header'}}[0];
967 my $source = @{$msg_hash->{'source'}}[0];
968 my $target = @{$msg_hash->{'target'}}[0];
969 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
970 my $error = 0;
971 my $hostId;
972 my $xml_msg;
974 # Build return message with twisted target and source
975 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
976 if (defined $forward_to_gosa) {
977 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
978 }
980 # Sanity check of needed parameter
981 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
982 $error++;
983 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
984 &add_content2xml_hash($out_hash, "error", "hostId");
985 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
986 }
988 if (not $error) {
990 # Get hostId
991 $hostId = @{$msg_hash->{'hostId'}}[0];
992 &add_content2xml_hash($out_hash, "hostId", "$hostId");
993 &add_content2xml_hash($out_hash, "xxx", "");
994 }
996 $xml_msg= &create_xml_string($out_hash);
998 if (not $error) {
1000 # JSON Query
1001 my $callobj = {
1002 method => 'getSoftwareInformation_hash',
1003 params => [ $hostId ],
1004 id => 1,
1005 };
1007 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1008 if (not &check_opsi_res($res)){
1009 my $result= $res->result;
1010 }
1012 $xml_msg=~ s/<xxx><\/xxx>//;
1014 }
1016 # Return message
1017 return ( $xml_msg );
1018 }
1021 ## @method opsi_get_local_products
1022 # Reports product for given hostId or globally.
1023 # @param msg - STRING - xml message with optional tag hostId
1024 # @param msg_hash - HASHREF - message information parsed into a hash
1025 # @param session_id - INTEGER - POE session id of the processing of this message
1026 # @return out_msg - STRING - feedback to GOsa in success and error case
1027 sub opsi_get_local_products {
1028 my ($msg, $msg_hash, $session_id) = @_;
1029 my $header = @{$msg_hash->{'header'}}[0];
1030 my $source = @{$msg_hash->{'source'}}[0];
1031 my $target = @{$msg_hash->{'target'}}[0];
1032 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1033 my $hostId;
1035 # Build return message with twisted target and source
1036 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1037 if (defined $forward_to_gosa) {
1038 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1039 }
1040 &add_content2xml_hash($out_hash, "xxx", "");
1042 # Get hostId if defined
1043 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
1044 $hostId = @{$msg_hash->{'hostId'}}[0];
1045 &add_content2xml_hash($out_hash, "hostId", $hostId);
1046 }
1048 # Move to XML string
1049 my $xml_msg= &create_xml_string($out_hash);
1051 # For hosts, only return the products that are or get installed
1052 my $callobj;
1053 $callobj = {
1054 method => 'getLocalBootProductIds_list',
1055 params => [ ],
1056 id => 1,
1057 };
1059 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1060 my %r = ();
1061 for (@{$res->result}) { $r{$_} = 1 }
1063 if (not &check_opsi_res($res)){
1065 if (defined $hostId){
1066 $callobj = {
1067 method => 'getProductStates_hash',
1068 params => [ $hostId ],
1069 id => 1,
1070 };
1072 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1073 if (not &check_opsi_res($hres)){
1074 my $htmp= $hres->result->{$hostId};
1076 # Check state != not_installed or action == setup -> load and add
1077 foreach my $product (@{$htmp}){
1079 if (!defined ($r{$product->{'productId'}})){
1080 next;
1081 }
1083 # Now we've a couple of hashes...
1084 if ($product->{'installationStatus'} ne "not_installed" or
1085 $product->{'actionRequest'} eq "setup"){
1086 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1088 $callobj = {
1089 method => 'getProduct_hash',
1090 params => [ $product->{'productId'} ],
1091 id => 1,
1092 };
1094 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1095 if (not &check_opsi_res($sres)){
1096 my $tres= $sres->result;
1098 my $name= xml_quote($tres->{'name'});
1099 my $r= $product->{'productId'};
1100 my $description= xml_quote($tres->{'description'});
1101 $name=~ s/\//\\\//;
1102 $description=~ s/\//\\\//;
1103 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1104 }
1106 }
1107 }
1109 }
1111 } else {
1112 foreach my $r (@{$res->result}) {
1113 $callobj = {
1114 method => 'getProduct_hash',
1115 params => [ $r ],
1116 id => 1,
1117 };
1119 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1120 if (not &check_opsi_res($sres)){
1121 my $tres= $sres->result;
1123 my $name= xml_quote($tres->{'name'});
1124 my $description= xml_quote($tres->{'description'});
1125 $name=~ s/\//\\\//;
1126 $description=~ s/\//\\\//;
1127 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1128 }
1130 }
1132 }
1133 }
1135 $xml_msg=~ s/<xxx><\/xxx>//;
1137 # Retrun Message
1138 return ( $xml_msg );
1139 }
1142 ## @method opsi_del_client
1143 # Deletes a client from Opsi.
1144 # @param msg - STRING - xml message with tag hostId
1145 # @param msg_hash - HASHREF - message information parsed into a hash
1146 # @param session_id - INTEGER - POE session id of the processing of this message
1147 # @return out_msg - STRING - feedback to GOsa in success and error case
1148 sub opsi_del_client {
1149 my ($msg, $msg_hash, $session_id) = @_;
1150 my $header = @{$msg_hash->{'header'}}[0];
1151 my $source = @{$msg_hash->{'source'}}[0];
1152 my $target = @{$msg_hash->{'target'}}[0];
1153 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1154 my $hostId;
1155 my $error = 0;
1157 # Build return message with twisted target and source
1158 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1159 if (defined $forward_to_gosa) {
1160 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1161 }
1163 # Sanity check of needed parameter
1164 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1165 $error++;
1166 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1167 &add_content2xml_hash($out_hash, "error", "hostId");
1168 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
1169 }
1171 if (not $error) {
1173 # Get hostId
1174 $hostId = @{$msg_hash->{'hostId'}}[0];
1175 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1177 # JSON Query
1178 my $callobj = {
1179 method => 'deleteClient',
1180 params => [ $hostId ],
1181 id => 1,
1182 };
1183 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1184 }
1186 # Move to XML string
1187 my $xml_msg= &create_xml_string($out_hash);
1189 # Return message
1190 return ( $xml_msg );
1191 }
1194 ## @method opsi_install_client
1195 # Set a client in Opsi to install and trigger a wake on lan message (WOL).
1196 # @param msg - STRING - xml message with tags hostId, macaddress
1197 # @param msg_hash - HASHREF - message information parsed into a hash
1198 # @param session_id - INTEGER - POE session id of the processing of this message
1199 # @return out_msg - STRING - feedback to GOsa in success and error case
1200 sub opsi_install_client {
1201 my ($msg, $msg_hash, $session_id) = @_;
1202 my $header = @{$msg_hash->{'header'}}[0];
1203 my $source = @{$msg_hash->{'source'}}[0];
1204 my $target = @{$msg_hash->{'target'}}[0];
1205 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1208 my ($hostId, $macaddress);
1210 my $error = 0;
1211 my @out_msg_l;
1213 # Build return message with twisted target and source
1214 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1215 if (defined $forward_to_gosa) {
1216 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1217 }
1219 # Sanity check of needed parameter
1220 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1221 $error++;
1222 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1223 &add_content2xml_hash($out_hash, "error", "hostId");
1224 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
1225 }
1226 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') ) {
1227 $error++;
1228 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1229 &add_content2xml_hash($out_hash, "error", "macaddress");
1230 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
1231 } else {
1232 if ((exists $msg_hash->{'macaddress'}) &&
1233 ($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)) {
1234 $macaddress = $1;
1235 } else {
1236 $error ++;
1237 &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1238 &add_content2xml_hash($out_hash, "error", "macaddress");
1239 &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1);
1240 }
1241 }
1243 if (not $error) {
1245 # Get hostId
1246 $hostId = @{$msg_hash->{'hostId'}}[0];
1247 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1249 # Load all products for this host with status != "not_installed" or actionRequest != "none"
1250 my $callobj = {
1251 method => 'getProductStates_hash',
1252 params => [ $hostId ],
1253 id => 1,
1254 };
1256 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1257 if (not &check_opsi_res($hres)){
1258 my $htmp= $hres->result->{$hostId};
1260 # check state != not_installed or action == setup -> load and add
1261 foreach my $product (@{$htmp}){
1262 # Now we've a couple of hashes...
1263 if ($product->{'installationStatus'} ne "not_installed" or
1264 $product->{'actionRequest'} ne "none"){
1266 # Do an action request for all these -> "setup".
1267 $callobj = {
1268 method => 'setProductActionRequest',
1269 params => [ $product->{'productId'}, $hostId, "setup" ],
1270 id => 1,
1271 };
1272 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1273 my ($res_err, $res_err_string) = &check_opsi_res($res);
1274 if ($res_err){
1275 &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1276 } else {
1277 &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1278 }
1279 }
1280 }
1281 }
1282 push(@out_msg_l, &create_xml_string($out_hash));
1285 # Build wakeup message for client
1286 if (not $error) {
1287 my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1288 &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1289 my $wakeup_msg = &create_xml_string($wakeup_hash);
1290 push(@out_msg_l, $wakeup_msg);
1292 # invoke trigger wake for this gosa-si-server
1293 &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1294 }
1295 }
1297 # Return messages
1298 return @out_msg_l;
1299 }
1302 ## @method _set_action
1303 # Set action for an Opsi client
1304 # @param product - STRING - Opsi product
1305 # @param action - STRING - action
1306 # @param hostId - STRING - Opsi hostId
1307 sub _set_action {
1308 my $product= shift;
1309 my $action = shift;
1310 my $hostId = shift;
1311 my $callobj;
1313 $callobj = {
1314 method => 'setProductActionRequest',
1315 params => [ $product, $hostId, $action],
1316 id => 1,
1317 };
1319 $main::opsi_client->call($main::opsi_url, $callobj);
1320 }
1322 ## @method _set_state
1323 # Set state for an Opsi client
1324 # @param product - STRING - Opsi product
1325 # @param action - STRING - state
1326 # @param hostId - STRING - Opsi hostId
1327 sub _set_state {
1328 my $product = shift;
1329 my $state = shift;
1330 my $hostId = shift;
1331 my $callobj;
1333 $callobj = {
1334 method => 'setProductState',
1335 params => [ $product, $hostId, $state ],
1336 id => 1,
1337 };
1339 $main::opsi_client->call($main::opsi_url, $callobj);
1340 }
1342 ################################
1343 #
1344 # @brief Create a license pool at Opsi server.
1345 # @param licensePoolId The name of the pool (optional).
1346 # @param description The description of the pool (optional).
1347 # @param productIds A list of assigned porducts of the pool (optional).
1348 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional).
1349 #
1350 sub opsi_createLicensePool {
1351 my ($msg, $msg_hash, $session_id) = @_;
1352 my $header = @{$msg_hash->{'header'}}[0];
1353 my $source = @{$msg_hash->{'source'}}[0];
1354 my $target = @{$msg_hash->{'target'}}[0];
1355 my $out_hash;
1356 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1357 my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1358 my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1359 my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1361 # Create license Pool
1362 my $callobj = {
1363 method => 'createLicensePool',
1364 params => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1365 id => 1,
1366 };
1367 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1369 # Check Opsi error
1370 my ($res_error, $res_error_str) = &check_opsi_res($res);
1371 if ($res_error){
1372 # Create error message
1373 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1374 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1375 return ( &create_xml_string($out_hash) );
1376 }
1378 # Create function result message
1379 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1381 return ( &create_xml_string($out_hash) );
1382 }
1384 ################################
1385 #
1386 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1387 #
1388 sub opsi_getLicensePools_listOfHashes {
1389 my ($msg, $msg_hash, $session_id) = @_;
1390 my $header = @{$msg_hash->{'header'}}[0];
1391 my $source = @{$msg_hash->{'source'}}[0];
1392 my $out_hash;
1394 # Fetch infos from Opsi server
1395 my $callobj = {
1396 method => 'getLicensePools_listOfHashes',
1397 params => [ ],
1398 id => 1,
1399 };
1400 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1402 # Check Opsi error
1403 my ($res_error, $res_error_str) = &check_opsi_res($res);
1404 if ($res_error){
1405 # Create error message
1406 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1407 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1408 return ( &create_xml_string($out_hash) );
1409 }
1411 # Create function result message
1412 my $res_hash = { 'hit'=> [] };
1413 foreach my $licensePool ( @{$res->result}) {
1414 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1415 'description' => [$licensePool->{'description'}],
1416 'productIds' => $licensePool->{'productIds'},
1417 'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1418 };
1419 push( @{$res_hash->{hit}}, $licensePool_hash );
1420 }
1422 # Create function result message
1423 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1424 $out_hash->{result} = [$res_hash];
1426 return ( &create_xml_string($out_hash) );
1427 }
1429 ################################
1430 #
1431 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1432 # @param licensePoolId The name of the pool.
1433 #
1434 sub opsi_getLicensePool_hash {
1435 my ($msg, $msg_hash, $session_id) = @_;
1436 my $header = @{$msg_hash->{'header'}}[0];
1437 my $source = @{$msg_hash->{'source'}}[0];
1438 my $target = @{$msg_hash->{'target'}}[0];
1439 my $licensePoolId;
1440 my $out_hash;
1442 # Check input sanity
1443 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1444 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1445 } else {
1446 return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
1447 }
1449 # Fetch infos from Opsi server
1450 my $callobj = {
1451 method => 'getLicensePool_hash',
1452 params => [ $licensePoolId ],
1453 id => 1,
1454 };
1455 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1457 # Check Opsi error
1458 my ($res_error, $res_error_str) = &check_opsi_res($res);
1459 if ($res_error){
1460 # Create error message
1461 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1462 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1463 &add_content2xml_hash($out_hash, "error", $res_error_str);
1464 return ( &create_xml_string($out_hash) );
1465 }
1467 # Create function result message
1468 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1469 &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1470 &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1471 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1472 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1474 return ( &create_xml_string($out_hash) );
1475 }
1477 sub _parse_getSoftwareLicenseUsages {
1478 my $res = shift;
1480 # Parse Opsi result
1481 my $tmp_licensePool_cache = {};
1482 my $res_hash = { 'hit'=> [] };
1483 foreach my $license ( @{$res}) {
1484 my $tmp_licensePool = $license->{'licensePoolId'};
1485 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1486 # Fetch missing informations from Opsi and cache the results for a possible later usage
1487 my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1488 if (not $err) {
1489 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1490 }
1491 }
1492 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1493 'notes' => [$license->{'notes'}],
1494 'licenseKey' => [$license->{'licenseKey'}],
1495 'hostId' => [$license->{'hostId'}],
1496 'licensePoolId' => [$tmp_licensePool],
1497 };
1498 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1499 $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1500 map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1501 map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1502 }
1503 push( @{$res_hash->{hit}}, $license_hash );
1504 }
1506 return $res_hash;
1507 }
1509 ################################
1510 #
1511 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1512 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1513 # @param licensePoolId The name of the pool (optional).
1514 #
1515 sub opsi_getSoftwareLicenseUsages {
1516 my ($msg, $msg_hash, $session_id) = @_;
1517 my $header = @{$msg_hash->{'header'}}[0];
1518 my $source = @{$msg_hash->{'source'}}[0];
1519 my $target = @{$msg_hash->{'target'}}[0];
1520 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1521 my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1522 my $out_hash;
1524 my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1525 if ($err){
1526 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1527 }
1529 # Parse Opsi result
1530 my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1532 # Create function result message
1533 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1534 $out_hash->{result} = [$res_hash];
1536 return ( &create_xml_string($out_hash) );
1537 }
1539 ################################
1540 #
1541 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1542 # @param productId Something like 'firefox', 'python' or anything else .
1543 #
1544 sub opsi_getSoftwareLicenseUsagesForProductId {
1545 my ($msg, $msg_hash, $session_id) = @_;
1546 my $header = @{$msg_hash->{'header'}}[0];
1547 my $source = @{$msg_hash->{'source'}}[0];
1549 # Check input sanity
1550 my $productId;
1551 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1552 $productId= @{$msg_hash->{'productId'}}[0];
1553 } else {
1554 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1555 }
1557 # Fetch licensePoolId for productId
1558 my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1559 if ($err){
1560 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1561 }
1563 my $licensePoolId;
1565 # Fetch softwareLiceceUsages for licensePoolId
1566 ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1567 if ($err){
1568 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1569 }
1571 # Parse Opsi result
1572 my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1574 # Create function result message
1575 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1576 $out_hash->{result} = [$res_hash];
1578 return ( &create_xml_string($out_hash) );
1579 }
1581 ################################
1582 #
1583 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1584 # @param softwareLicenseId Identificator of a license.
1585 #
1586 sub opsi_getSoftwareLicense_hash {
1587 my ($msg, $msg_hash, $session_id) = @_;
1588 my $header = @{$msg_hash->{'header'}}[0];
1589 my $source = @{$msg_hash->{'source'}}[0];
1590 my $target = @{$msg_hash->{'target'}}[0];
1591 my $softwareLicenseId;
1592 my $out_hash;
1594 # Check input sanity
1595 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1596 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1597 } else {
1598 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1599 }
1601 my $callobj = {
1602 method => 'getSoftwareLicense_hash',
1603 params => [ $softwareLicenseId ],
1604 id => 1,
1605 };
1606 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1608 # Check Opsi error
1609 my ($res_error, $res_error_str) = &check_opsi_res($res);
1610 if ($res_error){
1611 # Create error message
1612 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1613 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1614 return ( &create_xml_string($out_hash) );
1615 }
1617 # Create function result message
1618 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1619 &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1620 &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1621 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1622 &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1623 foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1624 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1625 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1626 }
1628 return ( &create_xml_string($out_hash) );
1629 }
1631 ################################
1632 #
1633 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool.
1634 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted.
1635 # @param licensePoolId The name of the pool.
1636 #
1637 sub opsi_deleteLicensePool {
1638 my ($msg, $msg_hash, $session_id) = @_;
1639 my $header = @{$msg_hash->{'header'}}[0];
1640 my $source = @{$msg_hash->{'source'}}[0];
1641 my $target = @{$msg_hash->{'target'}}[0];
1642 my $licensePoolId;
1643 my $out_hash;
1645 # Check input sanity
1646 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1647 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1648 } else {
1649 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1650 }
1652 # Fetch softwareLicenseIds used in license pool
1653 # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1654 # but not the license contracts of the software licenses. In our case each software license has exactly one license contract.
1655 my $callobj = {
1656 method => 'getSoftwareLicenses_listOfHashes',
1657 params => [ ],
1658 id => 1,
1659 };
1660 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1662 # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1663 my @lCI_toBeDeleted;
1664 foreach my $softwareLicenseHash ( @{$res->result} ) {
1665 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) {
1666 next;
1667 }
1668 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1669 }
1671 # Delete license pool at Opsi server
1672 $callobj = {
1673 method => 'deleteLicensePool',
1674 params => [ $licensePoolId, 'deleteLicenses=True' ],
1675 id => 1,
1676 };
1677 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1678 my ($res_error, $res_error_str) = &check_opsi_res($res);
1679 if ($res_error){
1680 # Create error message
1681 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1682 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1683 return ( &create_xml_string($out_hash) );
1684 }
1686 # Delete each license contract connected with the license pool
1687 foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1688 my $callobj = {
1689 method => 'deleteLicenseContract',
1690 params => [ $licenseContractId ],
1691 id => 1,
1692 };
1693 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1694 my ($res_error, $res_error_str) = &check_opsi_res($res);
1695 if ($res_error){
1696 # Create error message
1697 &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1698 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1699 return ( &create_xml_string($out_hash) );
1700 }
1701 }
1703 # Create function result message
1704 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1706 return ( &create_xml_string($out_hash) );
1707 }
1709 ################################
1710 #
1711 # @brief Create a license contract, create a software license and add the software license to the license pool
1712 # @param licensePoolId The name of the pool the license should be assigned.
1713 # @param licenseKey The license key.
1714 # @param partner Name of the license partner (optional).
1715 # @param conclusionDate Date of conclusion of license contract (optional)
1716 # @param notificationDate Date of notification that license is running out soon (optional).
1717 # @param notes This is the place for some notes (optional)
1718 # @param softwareLicenseId Identificator of a license (optional).
1719 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1720 # @param maxInstallations The number of clients use this license (optional).
1721 # @param boundToHost The name of the client the license is bound to (optional).
1722 # @param expirationDate The date when the license is running down (optional).
1723 #
1724 sub opsi_createLicense {
1725 my ($msg, $msg_hash, $session_id) = @_;
1726 my $header = @{$msg_hash->{'header'}}[0];
1727 my $source = @{$msg_hash->{'source'}}[0];
1728 my $target = @{$msg_hash->{'target'}}[0];
1729 my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1730 my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1731 my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1732 my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1733 my $licenseContractId = undef;
1734 my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1735 my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1736 my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1737 my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1738 my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1739 my $licensePoolId;
1740 my $licenseKey;
1741 my $out_hash;
1743 # Check input sanity
1744 if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1745 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1746 } else {
1747 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1748 }
1749 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1750 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1751 } else {
1752 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1753 }
1754 if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1755 return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
1756 }
1758 # Automatically define licenseContractId if ID is not given
1759 if (defined $softwareLicenseId) {
1760 $licenseContractId = "c_".$softwareLicenseId;
1761 }
1763 # Create license contract at Opsi server
1764 my $callobj = {
1765 method => 'createLicenseContract',
1766 params => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1767 id => 1,
1768 };
1769 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1771 # Check Opsi error
1772 my ($res_error, $res_error_str) = &check_opsi_res($res);
1773 if ($res_error){
1774 # Create error message
1775 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1776 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1777 return ( &create_xml_string($out_hash) );
1778 }
1780 $licenseContractId = $res->result;
1782 # Create software license at Opsi server
1783 $callobj = {
1784 method => 'createSoftwareLicense',
1785 params => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1786 id => 1,
1787 };
1788 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1790 # Check Opsi error
1791 ($res_error, $res_error_str) = &check_opsi_res($res);
1792 if ($res_error){
1793 # Create error message
1794 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1795 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1796 return ( &create_xml_string($out_hash) );
1797 }
1799 $softwareLicenseId = $res->result;
1801 # Add software license to license pool
1802 $callobj = {
1803 method => 'addSoftwareLicenseToLicensePool',
1804 params => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1805 id => 1,
1806 };
1807 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1809 # Check Opsi error
1810 ($res_error, $res_error_str) = &check_opsi_res($res);
1811 if ($res_error){
1812 # Create error message
1813 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1814 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1815 return ( &create_xml_string($out_hash) );
1816 }
1818 # Create function result message
1819 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1821 return ( &create_xml_string($out_hash) );
1822 }
1824 ################################
1825 #
1826 # @brief Assign a software license to a host
1827 # @param hostid Something like client_1.intranet.mydomain.de
1828 # @param licensePoolId The name of the pool.
1829 #
1830 sub opsi_assignSoftwareLicenseToHost {
1831 my ($msg, $msg_hash, $session_id) = @_;
1832 my $header = @{$msg_hash->{'header'}}[0];
1833 my $source = @{$msg_hash->{'source'}}[0];
1834 my $target = @{$msg_hash->{'target'}}[0];
1835 my $hostId;
1836 my $licensePoolId;
1838 # Check input sanity
1839 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1840 $hostId = @{$msg_hash->{'hostId'}}[0];
1841 } else {
1842 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1843 }
1844 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1845 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1846 } else {
1847 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1848 }
1850 # Assign a software license to a host
1851 my $callobj = {
1852 method => 'getAndAssignSoftwareLicenseKey',
1853 params => [ $hostId, $licensePoolId ],
1854 id => 1,
1855 };
1856 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1858 # Check Opsi error
1859 my ($res_error, $res_error_str) = &check_opsi_res($res);
1860 if ($res_error){
1861 # Create error message
1862 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1863 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1864 return ( &create_xml_string($out_hash) );
1865 }
1867 # Create function result message
1868 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1870 return ( &create_xml_string($out_hash) );
1871 }
1873 ################################
1874 #
1875 # @brief Unassign a software license from a host.
1876 # @param hostid Something like client_1.intranet.mydomain.de
1877 # @param licensePoolId The name of the pool.
1878 #
1879 sub opsi_unassignSoftwareLicenseFromHost {
1880 my ($msg, $msg_hash, $session_id) = @_;
1881 my $header = @{$msg_hash->{'header'}}[0];
1882 my $source = @{$msg_hash->{'source'}}[0];
1883 my $target = @{$msg_hash->{'target'}}[0];
1884 my $hostId;
1885 my $licensePoolId;
1887 # Check input sanity
1888 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1889 $hostId = @{$msg_hash->{'hostId'}}[0];
1890 } else {
1891 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1892 }
1893 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1894 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1895 } else {
1896 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1897 }
1899 # Unassign a software license from a host
1900 my $callobj = {
1901 method => 'deleteSoftwareLicenseUsage',
1902 params => [ $hostId, '', $licensePoolId ],
1903 id => 1,
1904 };
1905 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1907 # Check Opsi error
1908 my ($res_error, $res_error_str) = &check_opsi_res($res);
1909 if ($res_error){
1910 # Create error message
1911 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1912 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1913 return ( &create_xml_string($out_hash) );
1914 }
1916 # Create function result message
1917 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1919 return ( &create_xml_string($out_hash) );
1920 }
1922 ################################
1923 #
1924 # @brief Unassign all software licenses from a host
1925 # @param hostid Something like client_1.intranet.mydomain.de
1926 #
1927 sub opsi_unassignAllSoftwareLicensesFromHost {
1928 my ($msg, $msg_hash, $session_id) = @_;
1929 my $header = @{$msg_hash->{'header'}}[0];
1930 my $source = @{$msg_hash->{'source'}}[0];
1931 my $target = @{$msg_hash->{'target'}}[0];
1932 my $hostId;
1934 # Check input sanity
1935 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1936 $hostId = @{$msg_hash->{'hostId'}}[0];
1937 } else {
1938 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1939 }
1941 # Unassign all software licenses from a host
1942 my $callobj = {
1943 method => 'deleteAllSoftwareLicenseUsages',
1944 params => [ $hostId ],
1945 id => 1,
1946 };
1947 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1949 # Check Opsi error
1950 my ($res_error, $res_error_str) = &check_opsi_res($res);
1951 if ($res_error){
1952 # Create error message
1953 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1954 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1955 return ( &create_xml_string($out_hash) );
1956 }
1958 # Create function result message
1959 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1961 return ( &create_xml_string($out_hash) );
1962 }
1965 ################################
1966 #
1967 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1968 # and the number of max and remaining installations for a given OPSI product.
1969 # @param productId Identificator of an OPSI product.
1970 #
1971 sub opsi_getLicenseInformationForProduct {
1972 my ($msg, $msg_hash, $session_id) = @_;
1973 my $header = @{$msg_hash->{'header'}}[0];
1974 my $source = @{$msg_hash->{'source'}}[0];
1975 my $productId;
1976 my $out_hash;
1978 # Check input sanity
1979 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1980 $productId = @{$msg_hash->{'productId'}}[0];
1981 } else {
1982 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1983 }
1985 # Fetch infos from Opsi server
1986 my $callobj = {
1987 method => 'getLicensePoolId',
1988 params => [ $productId ],
1989 id => 1,
1990 };
1991 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1993 # Check Opsi error
1994 my ($res_error, $res_error_str) = &check_opsi_res($res);
1995 if ($res_error){
1996 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
1997 }
1999 my $licensePoolId = $res->result;
2001 # Fetch statistic information for given pool ID
2002 $callobj = {
2003 method => 'getLicenseStatistics_hash',
2004 params => [ ],
2005 id => 1,
2006 };
2007 $res = $main::opsi_client->call($main::opsi_url, $callobj);
2009 # Check Opsi error
2010 ($res_error, $res_error_str) = &check_opsi_res($res);
2011 if ($res_error){
2012 # Create error message
2013 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
2014 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
2015 return ( &create_xml_string($out_hash) );
2016 }
2018 # Create function result message
2019 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2020 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
2021 &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
2022 &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
2023 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
2024 &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
2025 map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
2027 return ( &create_xml_string($out_hash) );
2028 }
2031 ################################
2032 #
2033 # @brief
2034 # @param
2035 #
2036 sub opsi_getPool {
2037 my ($msg, $msg_hash, $session_id) = @_;
2038 my $header = @{$msg_hash->{'header'}}[0];
2039 my $source = @{$msg_hash->{'source'}}[0];
2041 # Check input sanity
2042 my $licensePoolId;
2043 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2044 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2045 } else {
2046 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2047 }
2049 # Create hash for the answer
2050 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2052 # Call Opsi
2053 my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2054 if ($err){
2055 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2056 }
2057 # Add data to outgoing hash
2058 &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2059 &add_content2xml_hash($out_hash, "description", $res->{'description'});
2060 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2061 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2064 # Call Opsi two times
2065 my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2066 if ($usages_err){
2067 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
2068 }
2069 my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2070 if ($licenses_err){
2071 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2072 }
2074 # Add data to outgoing hash
2075 # Parse through all software licenses and select those associated to the pool
2076 my $res_hash = { 'hit'=> [] };
2077 foreach my $license ( @$licenses_res) {
2078 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2079 my $found = 0;
2080 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2081 foreach my $lPI ( @licensePoolIds_list) {
2082 if ($lPI eq $licensePoolId) { $found++ }
2083 }
2084 if (not $found ) { next; };
2085 # Found matching licensePoolId
2086 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2087 'licenseKeys' => {},
2088 'expirationDate' => [$license->{'expirationDate'}],
2089 'boundToHost' => [$license->{'boundToHost'}],
2090 'maxInstallations' => [$license->{'maxInstallations'}],
2091 'licenseType' => [$license->{'licenseType'}],
2092 'licenseContractId' => [$license->{'licenseContractId'}],
2093 'licensePoolIds' => [],
2094 'hostIds' => [],
2095 };
2096 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2097 push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2098 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
2099 }
2100 foreach my $usage (@$usages_res) {
2101 # Search for hostIds with matching softwareLicenseId
2102 if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2103 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2104 }
2105 }
2107 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
2108 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
2109 if ($lContract_err){
2110 return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
2111 }
2112 $license_hash->{$license->{'licenseContractId'}} = [];
2113 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
2114 'notificationDate' => [$lContract_res->{notificationDate}],
2115 'notes' => [$lContract_res->{notes}],
2116 'exirationDate' => [$lContract_res->{expirationDate}],
2117 'partner' => [$lContract_res->{partner}],
2118 };
2119 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2121 push( @{$res_hash->{hit}}, $license_hash );
2122 }
2123 $out_hash->{licenses} = [$res_hash];
2125 return ( &create_xml_string($out_hash) );
2126 }
2129 ################################
2130 #
2131 # @brief Removes at first the software license from license pool and than deletes the software license.
2132 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2133 # @param softwareLicenseId
2134 # @param licensePoolId
2135 #
2136 sub opsi_removeLicense {
2137 my ($msg, $msg_hash, $session_id) = @_;
2138 my $header = @{$msg_hash->{'header'}}[0];
2139 my $source = @{$msg_hash->{'source'}}[0];
2141 # Check input sanity
2142 my $softwareLicenseId;
2143 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2144 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2145 } else {
2146 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2147 }
2148 my $licensePoolId;
2149 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2150 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2151 } else {
2152 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2153 }
2155 # Call Opsi
2156 my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2157 if ($err){
2158 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2159 }
2161 # Call Opsi
2162 ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2163 if ($err){
2164 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2165 }
2167 # Create hash for the answer
2168 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2169 return ( &create_xml_string($out_hash) );
2170 }
2173 ################################
2174 #
2175 # @brief
2176 # @param
2177 #
2178 sub opsi_getReservedLicenses {
2179 my ($msg, $msg_hash, $session_id) = @_;
2180 my $header = @{$msg_hash->{'header'}}[0];
2181 my $source = @{$msg_hash->{'source'}}[0];
2183 # Check input sanity
2184 my $hostId;
2185 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2186 $hostId = @{$msg_hash->{'hostId'}}[0];
2187 } else {
2188 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2189 }
2191 # Fetch informations from Opsi server
2192 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2193 if ($license_err){
2194 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2195 }
2198 # Parse result
2199 my $res_hash = { 'hit'=> [] };
2200 foreach my $license ( @$license_res) {
2201 if ($license->{boundToHost} ne $hostId) { next; }
2203 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2204 'maxInstallations' => [$license->{'maxInstallations'}],
2205 'boundToHost' => [$license->{'boundToHost'}],
2206 'expirationDate' => [$license->{'expirationDate'}],
2207 'licenseContractId' => [$license->{'licenseContractId'}],
2208 'licenseType' => [$license->{'licenseType'}],
2209 'licensePoolIds' => [],
2210 };
2212 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2213 # Fetch information for license pools containing a software license which is bound to given host
2214 my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2215 if ($pool_err){
2216 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2217 }
2219 # Add licensePool information to result hash
2220 push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2221 $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2222 map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2223 map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2224 }
2225 push( @{$res_hash->{hit}}, $license_hash );
2226 }
2227 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2228 $out_hash->{licenses} = [$res_hash];
2229 return ( &create_xml_string($out_hash) );
2231 return;
2232 }
2234 ################################
2235 #
2236 # @brief
2237 # @param
2238 #
2239 sub opsi_boundHostToLicense {
2240 my ($msg, $msg_hash, $session_id) = @_;
2241 my $header = @{$msg_hash->{'header'}}[0];
2242 my $source = @{$msg_hash->{'source'}}[0];
2244 # Check input sanity
2245 my $hostId;
2246 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2247 $hostId = @{$msg_hash->{'hostId'}}[0];
2248 } else {
2249 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2250 }
2251 my $softwareLicenseId;
2252 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2253 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2254 } else {
2255 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2256 }
2258 # Fetch informations from Opsi server
2259 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2260 if ($license_err){
2261 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2262 }
2264 # Memorize parameter for given softwareLicenseId
2265 my $licenseContractId;
2266 my $licenseType;
2267 my $maxInstallations;
2268 my $boundToHost;
2269 my $expirationDate = "";
2270 my $found;
2271 foreach my $license (@$license_res) {
2272 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2273 $licenseContractId = $license->{licenseContractId};
2274 $licenseType = $license->{licenseType};
2275 $maxInstallations = $license->{maxInstallations};
2276 $expirationDate = $license->{expirationDate};
2277 $found++;
2278 }
2280 if (not $found) {
2281 return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
2282 }
2284 # Set boundToHost option for a given software license
2285 my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId,
2286 'licenseContractId' => $licenseContractId,
2287 'licenseType' => $licenseType,
2288 'maxInstallations' => $maxInstallations,
2289 'boundToHost' => $hostId,
2290 'expirationDate' => $expirationDate);
2291 if ($bound_err) {
2292 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2293 }
2295 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2296 return ( &create_xml_string($out_hash) );
2297 }
2299 ################################
2300 #
2301 # @brief
2302 # @param
2303 #
2304 sub opsi_unboundHostFromLicense {
2305 # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2306 # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2307 # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2308 my ($msg, $msg_hash, $session_id) = @_;
2309 my $header = @{$msg_hash->{'header'}}[0];
2310 my $source = @{$msg_hash->{'source'}}[0];
2312 # Check input sanity
2313 my $softwareLicenseId;
2314 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2315 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2316 } else {
2317 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2318 }
2320 # Memorize parameter witch are required for this procedure
2321 my $licenseContractId;
2322 my $licenseType;
2323 my $maxInstallations;
2324 my $expirationDate;
2325 my $partner;
2326 my $conclusionDate;
2327 my $notificationDate;
2328 my $notes;
2329 my $licensePoolId;
2330 my $licenseKey;
2332 # Fetch license informations from Opsi server
2333 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2334 if ($license_err){
2335 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2336 }
2337 my $found = 0;
2338 foreach my $license (@$license_res) {
2339 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2340 $licenseContractId = $license->{licenseContractId};
2341 $licenseType = $license->{licenseType};
2342 $maxInstallations = $license->{maxInstallations};
2343 $expirationDate = $license->{expirationDate};
2344 $licensePoolId = @{$license->{licensePoolIds}}[0];
2345 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2346 $found++;
2347 }
2349 # Fetch contract informations from Opsi server
2350 my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2351 if ($contract_err){
2352 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2353 }
2354 $partner = $contract_res->{partner};
2355 $conclusionDate = $contract_res->{conclusionDate};
2356 $notificationDate = $contract_res->{notificationDate};
2357 $expirationDate = $contract_res->{expirationDate};
2358 $notes = $contract_res->{notes};
2360 # Delete software license
2361 my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2362 if ($err) {
2363 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2364 }
2366 # Recreate software license without boundToHost
2367 ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate,
2368 'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2369 if ($err) {
2370 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2371 }
2372 ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType,
2373 'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate );
2374 if ($err) {
2375 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2376 }
2377 ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2378 if ($err) {
2379 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2380 }
2382 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2383 return ( &create_xml_string($out_hash) );
2384 }
2386 ################################
2387 #
2388 # @brief
2389 # @param
2390 #
2391 sub opsi_getAllSoftwareLicenses {
2392 my ($msg, $msg_hash, $session_id) = @_;
2393 my $header = @{$msg_hash->{'header'}}[0];
2394 my $source = @{$msg_hash->{'source'}}[0];
2396 my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
2397 if ($err) {
2398 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
2399 }
2401 # Parse result
2402 my $res_hash = { 'hit'=> [] };
2403 foreach my $license ( @$res) {
2404 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2405 'maxInstallations' => [$license->{'maxInstallations'}],
2406 'boundToHost' => [$license->{'boundToHost'}],
2407 'expirationDate' => [$license->{'expirationDate'}],
2408 'licenseContractId' => [$license->{'licenseContractId'}],
2409 'licenseType' => [$license->{'licenseType'}],
2410 'licensePoolIds' => [],
2411 'licenseKeys'=> {}
2412 };
2413 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2414 push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2415 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
2416 }
2417 push( @{$res_hash->{hit}}, $license_hash );
2418 }
2420 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2421 $out_hash->{licenses} = [$res_hash];
2422 return ( &create_xml_string($out_hash) );
2423 }
2425 sub opsi_test {
2426 my ($msg, $msg_hash, $session_id) = @_;
2427 my $header = @{$msg_hash->{'header'}}[0];
2428 my $source = @{$msg_hash->{'source'}}[0];
2429 my $pram1 = @{$msg_hash->{'productId'}}[0];
2431 print STDERR Dumper $pram1;
2433 # Fetch infos from Opsi server
2434 my $callobj = {
2435 method => 'getLicensePoolId',
2436 params => [ $pram1 ],
2437 id => 1,
2438 };
2439 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2441 print STDERR Dumper $res;
2442 return ();
2443 }
2448 sub _getLicensePool_hash {
2449 my %arg = (
2450 'licensePoolId' => undef,
2451 @_,
2452 );
2454 if (not defined $arg{licensePoolId} ) {
2455 return ("function requires licensePoolId as parameter", 1);
2456 }
2458 # Fetch pool infos from Opsi server
2459 my $callobj = {
2460 method => 'getLicensePool_hash',
2461 params => [ $arg{licensePoolId} ],
2462 id => 1,
2463 };
2464 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2466 # Check Opsi error
2467 my ($res_error, $res_error_str) = &check_opsi_res($res);
2468 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2470 return ($res->result, 0);
2471 }
2473 sub _getSoftwareLicenses_listOfHashes {
2474 # Fetch licenses associated to the given pool
2475 my $callobj = {
2476 method => 'getSoftwareLicenses_listOfHashes',
2477 params => [ ],
2478 id => 1,
2479 };
2480 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2482 # Check Opsi error
2483 my ($res_error, $res_error_str) = &check_opsi_res($res);
2484 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2486 return ($res->result, 0);
2487 }
2489 sub _getSoftwareLicenseUsages_listOfHashes {
2490 my %arg = (
2491 'hostId' => "",
2492 'licensePoolId' => "",
2493 @_,
2494 );
2496 # Fetch pool infos from Opsi server
2497 my $callobj = {
2498 method => 'getSoftwareLicenseUsages_listOfHashes',
2499 params => [ $arg{hostId}, $arg{licensePoolId} ],
2500 id => 1,
2501 };
2502 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2504 # Check Opsi error
2505 my ($res_error, $res_error_str) = &check_opsi_res($res);
2506 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2508 return ($res->result, 0);
2509 }
2511 sub _removeSoftwareLicenseFromLicensePool {
2512 my %arg = (
2513 'softwareLicenseId' => undef,
2514 'licensePoolId' => undef,
2515 @_,
2516 );
2518 if (not defined $arg{softwareLicenseId} ) {
2519 return ("function requires softwareLicenseId as parameter", 1);
2520 }
2521 if (not defined $arg{licensePoolId} ) {
2522 return ("function requires licensePoolId as parameter", 1);
2523 }
2525 # Remove software license from license pool
2526 my $callobj = {
2527 method => 'removeSoftwareLicenseFromLicensePool',
2528 params => [ $arg{softwareLicenseId}, $arg{licensePoolId} ],
2529 id => 1,
2530 };
2531 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2533 # Check Opsi error
2534 my ($res_error, $res_error_str) = &check_opsi_res($res);
2535 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2537 return ($res->result, 0);
2538 }
2540 sub _deleteSoftwareLicense {
2541 my %arg = (
2542 'softwareLicenseId' => undef,
2543 'removeFromPools' => "false",
2544 @_,
2545 );
2547 if (not defined $arg{softwareLicenseId} ) {
2548 return ("function requires softwareLicenseId as parameter", 1);
2549 }
2550 my $removeFromPools = "";
2551 if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) {
2552 $removeFromPools = "removeFromPools";
2553 }
2555 # Fetch
2556 my $callobj = {
2557 method => 'deleteSoftwareLicense',
2558 params => [ $arg{softwareLicenseId}, $removeFromPools ],
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 _getLicensePoolId {
2571 my %arg = (
2572 'productId' => undef,
2573 @_,
2574 );
2576 if (not defined $arg{productId} ) {
2577 return ("function requires productId as parameter", 1);
2578 }
2580 my $callobj = {
2581 method => 'getLicensePoolId',
2582 params => [ $arg{productId} ],
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 _getLicenseContract_hash {
2595 my %arg = (
2596 'licenseContractId' => undef,
2597 @_,
2598 );
2600 if (not defined $arg{licenseContractId} ) {
2601 return ("function requires licenseContractId as parameter", 1);
2602 }
2604 my $callobj = {
2605 method => 'getLicenseContract_hash',
2606 params => [ $arg{licenseContractId} ],
2607 id => 1,
2608 };
2609 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2611 # Check Opsi error
2612 my ($res_error, $res_error_str) = &check_opsi_res($res);
2613 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2615 return ($res->result, 0);
2616 }
2618 sub _createLicenseContract {
2619 my %arg = (
2620 'licenseContractId' => undef,
2621 'partner' => undef,
2622 'conclusionDate' => undef,
2623 'notificationDate' => undef,
2624 'expirationDate' => undef,
2625 'notes' => undef,
2626 @_,
2627 );
2629 # Create license contract at Opsi server
2630 my $callobj = {
2631 method => 'createLicenseContract',
2632 params => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2633 id => 1,
2634 };
2635 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2637 # Check Opsi error
2638 my ($res_error, $res_error_str) = &check_opsi_res($res);
2639 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2641 return ($res->result, 0);
2642 }
2644 sub _createSoftwareLicense {
2645 my %arg = (
2646 'softwareLicenseId' => undef,
2647 'licenseContractId' => undef,
2648 'licenseType' => undef,
2649 'maxInstallations' => undef,
2650 'boundToHost' => undef,
2651 'expirationDate' => undef,
2652 @_,
2653 );
2655 my $callobj = {
2656 method => 'createSoftwareLicense',
2657 params => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2658 id => 1,
2659 };
2660 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2662 # Check Opsi error
2663 my ($res_error, $res_error_str) = &check_opsi_res($res);
2664 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2666 return ($res->result, 0);
2667 }
2669 sub _addSoftwareLicenseToLicensePool {
2670 my %arg = (
2671 'softwareLicenseId' => undef,
2672 'licensePoolId' => undef,
2673 'licenseKey' => undef,
2674 @_,
2675 );
2677 if (not defined $arg{softwareLicenseId} ) {
2678 return ("function requires softwareLicenseId as parameter", 1);
2679 }
2680 if (not defined $arg{licensePoolId} ) {
2681 return ("function requires licensePoolId as parameter", 1);
2682 }
2684 # Add software license to license pool
2685 my $callobj = {
2686 method => 'addSoftwareLicenseToLicensePool',
2687 params => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ],
2688 id => 1,
2689 };
2690 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2692 # Check Opsi error
2693 my ($res_error, $res_error_str) = &check_opsi_res($res);
2694 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2696 return ($res->result, 0);
2697 }
2699 1;