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_listOfHashes",
33 "opsi_getLicensePools_listOfHashes",
34 "opsi_getLicenseInformationForProduct",
35 "opsi_getPool",
36 "opsi_removeLicense",
37 "opsi_test",
38 );
39 @EXPORT = @events;
41 use strict;
42 use warnings;
43 use GOSA::GosaSupportDaemon;
44 use Data::Dumper;
45 use XML::Quote qw(:all);
47 BEGIN {}
49 END {}
51 # ----------------------------------------------------------------------------
52 # D E C L A R A T I O N S
53 # ----------------------------------------------------------------------------
55 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
59 # ----------------------------------------------------------------------------
60 # S U B R O U T I N E S
61 # ----------------------------------------------------------------------------
64 ################################
65 #
66 # @brief A function returning a list of functions which are exported by importing the module.
67 # @return List of all provided functions
68 #
69 sub get_events {
70 return \@events;
71 }
73 ################################
74 #
75 # @brief Checks if there is a specified tag and if the the tag has a content.
76 # @return 0|1
77 #
78 sub _check_xml_tag_is_ok {
79 my ($msg_hash,$tag) = @_;
80 if (not defined $msg_hash->{$tag}) {
81 $_ = "message contains no tag '$tag'";
82 return 0;
83 }
84 if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
85 $_ = "message tag '$tag' has no content";
86 return 0;
87 }
88 return 1;
89 }
91 ################################
92 #
93 # @brief Writes the log line and returns the error message for GOsa.
94 #
95 sub _give_feedback {
96 my ($msg, $msg_hash, $session_id, $error) = @_;
97 &main::daemon_log("$session_id ERROR: $error: ".$msg, 1);
98 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{'source'}}[0], $error);
99 return &create_xml_string($out_hash);
100 }
102 ## @method opsi_add_product_to_client
103 # Adds an Opsi product to an Opsi client.
104 # @param msg - STRING - xml message with tags hostId and productId
105 # @param msg_hash - HASHREF - message information parsed into a hash
106 # @param session_id - INTEGER - POE session id of the processing of this message
107 # @return out_msg - STRING - feedback to GOsa in success and error case
108 sub opsi_add_product_to_client {
109 my ($msg, $msg_hash, $session_id) = @_;
110 my $header = @{$msg_hash->{'header'}}[0];
111 my $source = @{$msg_hash->{'source'}}[0];
112 my $target = @{$msg_hash->{'target'}}[0];
113 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
114 my ($hostId, $productId);
115 my $error = 0;
117 # Build return message
118 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
119 if (defined $forward_to_gosa) {
120 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
121 }
123 # Sanity check of needed parameter
124 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
125 $error++;
126 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
127 &add_content2xml_hash($out_hash, "error", "hostId");
128 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
130 }
131 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
132 $error++;
133 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
134 &add_content2xml_hash($out_hash, "error", "productId");
135 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
136 }
138 if (not $error) {
139 # Get hostId
140 $hostId = @{$msg_hash->{'hostId'}}[0];
141 &add_content2xml_hash($out_hash, "hostId", $hostId);
143 # Get productID
144 $productId = @{$msg_hash->{'productId'}}[0];
145 &add_content2xml_hash($out_hash, "productId", $productId);
147 # Do an action request for all these -> "setup".
148 my $callobj = {
149 method => 'setProductActionRequest',
150 params => [ $productId, $hostId, "setup" ],
151 id => 1, };
153 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
154 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
155 if ($sres_err){
156 &main::daemon_log("$session_id ERROR: cannot add product: ".$sres_err_string, 1);
157 &add_content2xml_hash($out_hash, "error", $sres_err_string);
158 }
159 }
161 # return message
162 return ( &create_xml_string($out_hash) );
163 }
165 ## @method opsi_del_product_from_client
166 # Deletes an Opsi-product from an Opsi-client.
167 # @param msg - STRING - xml message with tags hostId and productId
168 # @param msg_hash - HASHREF - message information parsed into a hash
169 # @param session_id - INTEGER - POE session id of the processing of this message
170 # @return out_msg - STRING - feedback to GOsa in success and error case
171 sub opsi_del_product_from_client {
172 my ($msg, $msg_hash, $session_id) = @_;
173 my $header = @{$msg_hash->{'header'}}[0];
174 my $source = @{$msg_hash->{'source'}}[0];
175 my $target = @{$msg_hash->{'target'}}[0];
176 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
177 my ($hostId, $productId);
178 my $error = 0;
179 my ($sres, $sres_err, $sres_err_string);
181 # Build return message
182 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
183 if (defined $forward_to_gosa) {
184 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
185 }
187 # Sanity check of needed parameter
188 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
189 $error++;
190 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
191 &add_content2xml_hash($out_hash, "error", "hostId");
192 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
194 }
195 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
196 $error++;
197 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
198 &add_content2xml_hash($out_hash, "error", "productId");
199 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
200 }
202 # All parameter available
203 if (not $error) {
204 # Get hostId
205 $hostId = @{$msg_hash->{'hostId'}}[0];
206 &add_content2xml_hash($out_hash, "hostId", $hostId);
208 # Get productID
209 $productId = @{$msg_hash->{'productId'}}[0];
210 &add_content2xml_hash($out_hash, "productId", $productId);
213 # : check the results for more than one entry which is currently installed
214 #$callobj = {
215 # method => 'getProductDependencies_listOfHashes',
216 # params => [ $productId ],
217 # id => 1, };
218 #
219 #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
220 #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
221 #if ($sres_err){
222 # &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
223 # &add_content2xml_hash($out_hash, "error", $sres_err_string);
224 # return ( &create_xml_string($out_hash) );
225 #}
228 # Check to get product action list
229 my $callobj = {
230 method => 'getPossibleProductActions_list',
231 params => [ $productId ],
232 id => 1, };
233 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
234 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
235 if ($sres_err){
236 &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
237 &add_content2xml_hash($out_hash, "error", $sres_err_string);
238 $error++;
239 }
240 }
242 # Check action uninstall of product
243 if (not $error) {
244 my $uninst_possible= 0;
245 foreach my $r (@{$sres->result}) {
246 if ($r eq 'uninstall') {
247 $uninst_possible= 1;
248 }
249 }
250 if (!$uninst_possible){
251 &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
252 &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
253 $error++;
254 }
255 }
257 # Set product state to "none"
258 # Do an action request for all these -> "setup".
259 if (not $error) {
260 my $callobj = {
261 method => 'setProductActionRequest',
262 params => [ $productId, $hostId, "none" ],
263 id => 1,
264 };
265 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
266 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
267 if ($sres_err){
268 &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
269 &add_content2xml_hash($out_hash, "error", $sres_err_string);
270 }
271 }
273 # Return message
274 return ( &create_xml_string($out_hash) );
275 }
277 ## @method opsi_add_client
278 # Adds an Opsi client to Opsi.
279 # @param msg - STRING - xml message with tags hostId and macaddress
280 # @param msg_hash - HASHREF - message information parsed into a hash
281 # @param session_id - INTEGER - POE session id of the processing of this message
282 # @return out_msg - STRING - feedback to GOsa in success and error case
283 sub opsi_add_client {
284 my ($msg, $msg_hash, $session_id) = @_;
285 my $header = @{$msg_hash->{'header'}}[0];
286 my $source = @{$msg_hash->{'source'}}[0];
287 my $target = @{$msg_hash->{'target'}}[0];
288 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
289 my ($hostId, $mac);
290 my $error = 0;
291 my ($sres, $sres_err, $sres_err_string);
293 # Build return message with twisted target and source
294 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
295 if (defined $forward_to_gosa) {
296 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
297 }
299 # Sanity check of needed parameter
300 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
301 $error++;
302 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
303 &add_content2xml_hash($out_hash, "error", "hostId");
304 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
305 }
306 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH')) {
307 $error++;
308 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
309 &add_content2xml_hash($out_hash, "error", "macaddress");
310 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
311 }
313 if (not $error) {
314 # Get hostId
315 $hostId = @{$msg_hash->{'hostId'}}[0];
316 &add_content2xml_hash($out_hash, "hostId", $hostId);
318 # Get macaddress
319 $mac = @{$msg_hash->{'macaddress'}}[0];
320 &add_content2xml_hash($out_hash, "macaddress", $mac);
322 my $name= $hostId;
323 $name=~ s/^([^.]+).*$/$1/;
324 my $domain= $hostId;
325 $domain=~ s/^[^.]+\.(.*)$/$1/;
326 my ($description, $notes, $ip);
328 if (defined @{$msg_hash->{'description'}}[0]){
329 $description = @{$msg_hash->{'description'}}[0];
330 }
331 if (defined @{$msg_hash->{'notes'}}[0]){
332 $notes = @{$msg_hash->{'notes'}}[0];
333 }
334 if (defined @{$msg_hash->{'ip'}}[0]){
335 $ip = @{$msg_hash->{'ip'}}[0];
336 }
338 my $callobj;
339 $callobj = {
340 method => 'createClient',
341 params => [ $name, $domain, $description, $notes, $ip, $mac ],
342 id => 1,
343 };
345 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
346 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
347 if ($sres_err){
348 &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
349 &add_content2xml_hash($out_hash, "error", $sres_err_string);
350 } else {
351 &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5);
352 }
353 }
355 # Return message
356 return ( &create_xml_string($out_hash) );
357 }
359 ## @method opsi_modify_client
360 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
361 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
362 # @param msg_hash - HASHREF - message information parsed into a hash
363 # @param session_id - INTEGER - POE session id of the processing of this message
364 # @return out_msg - STRING - feedback to GOsa in success and error case
365 sub opsi_modify_client {
366 my ($msg, $msg_hash, $session_id) = @_;
367 my $header = @{$msg_hash->{'header'}}[0];
368 my $source = @{$msg_hash->{'source'}}[0];
369 my $target = @{$msg_hash->{'target'}}[0];
370 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
371 my $hostId;
372 my $error = 0;
373 my ($sres, $sres_err, $sres_err_string);
375 # Build return message with twisted target and source
376 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
377 if (defined $forward_to_gosa) {
378 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
379 }
381 # Sanity check of needed parameter
382 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
383 $error++;
384 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
385 &add_content2xml_hash($out_hash, "error", "hostId");
386 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
387 }
389 if (not $error) {
390 # Get hostId
391 $hostId = @{$msg_hash->{'hostId'}}[0];
392 &add_content2xml_hash($out_hash, "hostId", $hostId);
393 my $name= $hostId;
394 $name=~ s/^([^.]+).*$/$1/;
395 my $domain= $hostId;
396 $domain=~ s/^[^.]+(.*)$/$1/;
398 # Modify description, notes or mac if defined
399 my ($description, $notes, $mac);
400 my $callobj;
401 if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
402 $description = @{$msg_hash->{'description'}}[0];
403 $callobj = {
404 method => 'setHostDescription',
405 params => [ $hostId, $description ],
406 id => 1,
407 };
408 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
409 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
410 if ($sres_err){
411 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
412 &add_content2xml_hash($out_hash, "error", $sres_err_string);
413 }
414 }
415 if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
416 $notes = @{$msg_hash->{'notes'}}[0];
417 $callobj = {
418 method => 'setHostNotes',
419 params => [ $hostId, $notes ],
420 id => 1,
421 };
422 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
423 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
424 if ($sres_err){
425 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
426 &add_content2xml_hash($out_hash, "error", $sres_err_string);
427 }
428 }
429 if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
430 $mac = @{$msg_hash->{'mac'}}[0];
431 $callobj = {
432 method => 'setMacAddress',
433 params => [ $hostId, $mac ],
434 id => 1,
435 };
436 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
437 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
438 if ($sres_err){
439 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
440 &add_content2xml_hash($out_hash, "error", $sres_err_string);
441 }
442 }
443 }
445 # Return message
446 return ( &create_xml_string($out_hash) );
447 }
450 ## @method opsi_get_netboot_products
451 # Get netboot products for specific host.
452 # @param msg - STRING - xml message with tag hostId
453 # @param msg_hash - HASHREF - message information parsed into a hash
454 # @param session_id - INTEGER - POE session id of the processing of this message
455 # @return out_msg - STRING - feedback to GOsa in success and error case
456 sub opsi_get_netboot_products {
457 my ($msg, $msg_hash, $session_id) = @_;
458 my $header = @{$msg_hash->{'header'}}[0];
459 my $source = @{$msg_hash->{'source'}}[0];
460 my $target = @{$msg_hash->{'target'}}[0];
461 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
462 my $hostId;
463 my $xml_msg;
465 # Build return message with twisted target and source
466 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
467 if (defined $forward_to_gosa) {
468 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
469 }
471 # Get hostId if defined
472 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
473 $hostId = @{$msg_hash->{'hostId'}}[0];
474 &add_content2xml_hash($out_hash, "hostId", $hostId);
475 }
477 &add_content2xml_hash($out_hash, "xxx", "");
478 $xml_msg = &create_xml_string($out_hash);
479 # For hosts, only return the products that are or get installed
480 my $callobj;
481 $callobj = {
482 method => 'getNetBootProductIds_list',
483 params => [ ],
484 id => 1,
485 };
486 &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
487 &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
488 &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
489 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
490 &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
491 my %r = ();
492 for (@{$res->result}) { $r{$_} = 1 }
494 if (not &check_opsi_res($res)){
496 if (defined $hostId){
498 $callobj = {
499 method => 'getProductStates_hash',
500 params => [ $hostId ],
501 id => 1,
502 };
504 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
505 if (not &check_opsi_res($hres)){
506 my $htmp= $hres->result->{$hostId};
508 # check state != not_installed or action == setup -> load and add
509 foreach my $product (@{$htmp}){
511 if (!defined ($r{$product->{'productId'}})){
512 next;
513 }
515 # Now we've a couple of hashes...
516 if ($product->{'installationStatus'} ne "not_installed" or
517 $product->{'actionRequest'} eq "setup"){
518 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
520 $callobj = {
521 method => 'getProduct_hash',
522 params => [ $product->{'productId'} ],
523 id => 1,
524 };
526 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
527 if (not &check_opsi_res($sres)){
528 my $tres= $sres->result;
530 my $name= xml_quote($tres->{'name'});
531 my $r= $product->{'productId'};
532 my $description= xml_quote($tres->{'description'});
533 $name=~ s/\//\\\//;
534 $description=~ s/\//\\\//;
535 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
536 }
537 }
538 }
540 }
542 } else {
543 foreach my $r (@{$res->result}) {
544 $callobj = {
545 method => 'getProduct_hash',
546 params => [ $r ],
547 id => 1,
548 };
550 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
551 if (not &check_opsi_res($sres)){
552 my $tres= $sres->result;
554 my $name= xml_quote($tres->{'name'});
555 my $description= xml_quote($tres->{'description'});
556 $name=~ s/\//\\\//;
557 $description=~ s/\//\\\//;
558 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
559 }
560 }
562 }
563 }
564 $xml_msg=~ s/<xxx><\/xxx>//;
566 # Return message
567 return ( $xml_msg );
568 }
571 ## @method opsi_get_product_properties
572 # Get product properties for a product and a specific host or gobally for a product.
573 # @param msg - STRING - xml message with tags productId and optional hostId
574 # @param msg_hash - HASHREF - message information parsed into a hash
575 # @param session_id - INTEGER - POE session id of the processing of this message
576 # @return out_msg - STRING - feedback to GOsa in success and error case
577 sub opsi_get_product_properties {
578 my ($msg, $msg_hash, $session_id) = @_;
579 my $header = @{$msg_hash->{'header'}}[0];
580 my $source = @{$msg_hash->{'source'}}[0];
581 my $target = @{$msg_hash->{'target'}}[0];
582 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
583 my ($hostId, $productId);
584 my $xml_msg;
586 # Build return message with twisted target and source
587 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
588 if (defined $forward_to_gosa) {
589 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
590 }
592 # Sanity check of needed parameter
593 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
594 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
595 &add_content2xml_hash($out_hash, "error", "productId");
596 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
598 # Return message
599 return ( &create_xml_string($out_hash) );
600 }
602 # Get productid
603 $productId = @{$msg_hash->{'productId'}}[0];
604 &add_content2xml_hash($out_hash, "producId", "$productId");
606 # Get hostId if defined
607 if (defined @{$msg_hash->{'hostId'}}[0]){
608 $hostId = @{$msg_hash->{'hostId'}}[0];
609 &add_content2xml_hash($out_hash, "hostId", $hostId);
610 }
612 # Load actions
613 my $callobj = {
614 method => 'getPossibleProductActions_list',
615 params => [ $productId ],
616 id => 1,
617 };
618 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
619 if (not &check_opsi_res($res)){
620 foreach my $action (@{$res->result}){
621 &add_content2xml_hash($out_hash, "action", $action);
622 }
623 }
625 # Add place holder
626 &add_content2xml_hash($out_hash, "xxx", "");
628 # Move to XML string
629 $xml_msg= &create_xml_string($out_hash);
631 # JSON Query
632 if (defined $hostId){
633 $callobj = {
634 method => 'getProductProperties_hash',
635 params => [ $productId, $hostId ],
636 id => 1,
637 };
638 } else {
639 $callobj = {
640 method => 'getProductProperties_hash',
641 params => [ $productId ],
642 id => 1,
643 };
644 }
645 $res = $main::opsi_client->call($main::opsi_url, $callobj);
647 # JSON Query 2
648 $callobj = {
649 method => 'getProductPropertyDefinitions_listOfHashes',
650 params => [ $productId ],
651 id => 1,
652 };
654 # Assemble options
655 my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
656 my $values = {};
657 my $descriptions = {};
658 if (not &check_opsi_res($res2)){
659 my $r= $res2->result;
661 foreach my $entr (@$r){
662 # Unroll values
663 my $cnv;
664 if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
665 foreach my $v (@{$entr->{'values'}}){
666 $cnv.= "<value>$v</value>";
667 }
668 } else {
669 $cnv= $entr->{'values'};
670 }
671 $values->{$entr->{'name'}}= $cnv;
672 $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
673 }
674 }
676 if (not &check_opsi_res($res)){
677 my $r= $res->result;
678 foreach my $key (keys %{$r}) {
679 my $item= "\n<item>";
680 my $value= $r->{$key};
681 my $dsc= "";
682 my $vals= "";
683 if (defined $descriptions->{$key}){
684 $dsc= $descriptions->{$key};
685 }
686 if (defined $values->{$key}){
687 $vals= $values->{$key};
688 }
689 $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
690 $item.= "</item>";
691 $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
692 }
693 }
695 $xml_msg=~ s/<xxx><\/xxx>//;
697 # Return message
698 return ( $xml_msg );
699 }
702 ## @method opsi_set_product_properties
703 # 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.
704 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
705 # @param msg_hash - HASHREF - message information parsed into a hash
706 # @param session_id - INTEGER - POE session id of the processing of this message
707 # @return out_msg - STRING - feedback to GOsa in success and error case
708 sub opsi_set_product_properties {
709 my ($msg, $msg_hash, $session_id) = @_;
710 my $header = @{$msg_hash->{'header'}}[0];
711 my $source = @{$msg_hash->{'source'}}[0];
712 my $target = @{$msg_hash->{'target'}}[0];
713 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
714 my ($productId, $hostId);
716 # Build return message with twisted target and source
717 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
718 if (defined $forward_to_gosa) {
719 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
720 }
722 # Sanity check of needed parameter
723 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
724 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
725 &add_content2xml_hash($out_hash, "error", "productId");
726 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
727 return ( &create_xml_string($out_hash) );
728 }
729 if (not exists $msg_hash->{'item'}) {
730 &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
731 &add_content2xml_hash($out_hash, "error", "item");
732 &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1);
733 return ( &create_xml_string($out_hash) );
734 } else {
735 if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
736 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
737 &add_content2xml_hash($out_hash, "error", "name");
738 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1);
739 return ( &create_xml_string($out_hash) );
740 }
741 if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
742 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
743 &add_content2xml_hash($out_hash, "error", "value");
744 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1);
745 return ( &create_xml_string($out_hash) );
746 }
747 }
748 # if no hostId is given, set_product_properties will act on globally
749 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1)) {
750 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
751 &add_content2xml_hash($out_hash, "error", "hostId");
752 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
753 return ( &create_xml_string($out_hash) );
754 }
757 # Get productId
758 $productId = @{$msg_hash->{'productId'}}[0];
759 &add_content2xml_hash($out_hash, "productId", $productId);
761 # Get hostId if defined
762 if (exists $msg_hash->{'hostId'}){
763 $hostId = @{$msg_hash->{'hostId'}}[0];
764 &add_content2xml_hash($out_hash, "hostId", $hostId);
765 }
767 # Set product states if requested
768 if (defined @{$msg_hash->{'action'}}[0]){
769 &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
770 }
771 if (defined @{$msg_hash->{'state'}}[0]){
772 &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
773 }
775 # Find properties
776 foreach my $item (@{$msg_hash->{'item'}}){
777 # JSON Query
778 my $callobj;
780 if (defined $hostId){
781 $callobj = {
782 method => 'setProductProperty',
783 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
784 id => 1,
785 };
786 } else {
787 $callobj = {
788 method => 'setProductProperty',
789 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
790 id => 1,
791 };
792 }
794 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
795 my ($res_err, $res_err_string) = &check_opsi_res($res);
797 if ($res_err){
798 &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
799 &add_content2xml_hash($out_hash, "error", $res_err_string);
800 }
801 }
804 # Return message
805 return ( &create_xml_string($out_hash) );
806 }
809 ## @method opsi_get_client_hardware
810 # Reports client hardware inventory.
811 # @param msg - STRING - xml message with tag hostId
812 # @param msg_hash - HASHREF - message information parsed into a hash
813 # @param session_id - INTEGER - POE session id of the processing of this message
814 # @return out_msg - STRING - feedback to GOsa in success and error case
815 sub opsi_get_client_hardware {
816 my ($msg, $msg_hash, $session_id) = @_;
817 my $header = @{$msg_hash->{'header'}}[0];
818 my $source = @{$msg_hash->{'source'}}[0];
819 my $target = @{$msg_hash->{'target'}}[0];
820 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
821 my $hostId;
822 my $error = 0;
823 my $xml_msg;
825 # Build return message with twisted target and source
826 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
827 if (defined $forward_to_gosa) {
828 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
829 }
831 # Sanity check of needed parameter
832 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
833 $error++;
834 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
835 &add_content2xml_hash($out_hash, "error", "hostId");
836 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
837 }
839 if (not $error) {
841 # Get hostId
842 $hostId = @{$msg_hash->{'hostId'}}[0];
843 &add_content2xml_hash($out_hash, "hostId", "$hostId");
844 &add_content2xml_hash($out_hash, "xxx", "");
845 }
847 # Move to XML string
848 $xml_msg= &create_xml_string($out_hash);
850 if (not $error) {
852 # JSON Query
853 my $callobj = {
854 method => 'getHardwareInformation_hash',
855 params => [ $hostId ],
856 id => 1,
857 };
859 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
860 if (not &check_opsi_res($res)){
861 my $result= $res->result;
862 if (ref $result eq "HASH") {
863 foreach my $r (keys %{$result}){
864 my $item= "\n<item><id>".xml_quote($r)."</id>";
865 my $value= $result->{$r};
866 foreach my $sres (@{$value}){
868 foreach my $dres (keys %{$sres}){
869 if (defined $sres->{$dres}){
870 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
871 }
872 }
874 }
875 $item.= "</item>";
876 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
878 }
879 }
880 }
882 $xml_msg=~ s/<xxx><\/xxx>//;
884 }
886 # Return message
887 return ( $xml_msg );
888 }
891 ## @method opsi_list_clients
892 # Reports all Opsi clients.
893 # @param msg - STRING - xml message
894 # @param msg_hash - HASHREF - message information parsed into a hash
895 # @param session_id - INTEGER - POE session id of the processing of this message
896 # @return out_msg - STRING - feedback to GOsa in success and error case
897 sub opsi_list_clients {
898 my ($msg, $msg_hash, $session_id) = @_;
899 my $header = @{$msg_hash->{'header'}}[0];
900 my $source = @{$msg_hash->{'source'}}[0];
901 my $target = @{$msg_hash->{'target'}}[0];
902 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
904 # Build return message with twisted target and source
905 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
906 if (defined $forward_to_gosa) {
907 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
908 }
909 &add_content2xml_hash($out_hash, "xxx", "");
911 # Move to XML string
912 my $xml_msg= &create_xml_string($out_hash);
914 # JSON Query
915 my $callobj = {
916 method => 'getClients_listOfHashes',
917 params => [ ],
918 id => 1,
919 };
920 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
921 if (not &check_opsi_res($res)){
922 foreach my $host (@{$res->result}){
923 my $item= "\n<item><name>".$host->{'hostId'}."</name>";
924 if (defined($host->{'description'})){
925 $item.= "<description>".xml_quote($host->{'description'})."</description>";
926 }
927 if (defined($host->{'notes'})){
928 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
929 }
930 if (defined($host->{'lastSeen'})){
931 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
932 }
934 $callobj = {
935 method => 'getIpAddress',
936 params => [ $host->{'hostId'} ],
937 id => 1,
938 };
939 my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
940 if ( not &check_opsi_res($sres)){
941 $item.= "<ip>".xml_quote($sres->result)."</ip>";
942 }
944 $callobj = {
945 method => 'getMacAddress',
946 params => [ $host->{'hostId'} ],
947 id => 1,
948 };
949 $sres= $main::opsi_client->call($main::opsi_url, $callobj);
950 if ( not &check_opsi_res($sres)){
951 $item.= "<mac>".xml_quote($sres->result)."</mac>";
952 }
953 $item.= "</item>";
954 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
955 }
956 }
958 $xml_msg=~ s/<xxx><\/xxx>//;
959 return ( $xml_msg );
960 }
964 ## @method opsi_get_client_software
965 # Reports client software inventory.
966 # @param msg - STRING - xml message with tag hostId
967 # @param msg_hash - HASHREF - message information parsed into a hash
968 # @param session_id - INTEGER - POE session id of the processing of this message
969 # @return out_msg - STRING - feedback to GOsa in success and error case
970 sub opsi_get_client_software {
971 my ($msg, $msg_hash, $session_id) = @_;
972 my $header = @{$msg_hash->{'header'}}[0];
973 my $source = @{$msg_hash->{'source'}}[0];
974 my $target = @{$msg_hash->{'target'}}[0];
975 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
976 my $error = 0;
977 my $hostId;
978 my $xml_msg;
980 # Build return message with twisted target and source
981 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
982 if (defined $forward_to_gosa) {
983 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
984 }
986 # Sanity check of needed parameter
987 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
988 $error++;
989 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
990 &add_content2xml_hash($out_hash, "error", "hostId");
991 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
992 }
994 if (not $error) {
996 # Get hostId
997 $hostId = @{$msg_hash->{'hostId'}}[0];
998 &add_content2xml_hash($out_hash, "hostId", "$hostId");
999 &add_content2xml_hash($out_hash, "xxx", "");
1000 }
1002 $xml_msg= &create_xml_string($out_hash);
1004 if (not $error) {
1006 # JSON Query
1007 my $callobj = {
1008 method => 'getSoftwareInformation_hash',
1009 params => [ $hostId ],
1010 id => 1,
1011 };
1013 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1014 if (not &check_opsi_res($res)){
1015 my $result= $res->result;
1016 }
1018 $xml_msg=~ s/<xxx><\/xxx>//;
1020 }
1022 # Return message
1023 return ( $xml_msg );
1024 }
1027 ## @method opsi_get_local_products
1028 # Reports product for given hostId or globally.
1029 # @param msg - STRING - xml message with optional tag hostId
1030 # @param msg_hash - HASHREF - message information parsed into a hash
1031 # @param session_id - INTEGER - POE session id of the processing of this message
1032 # @return out_msg - STRING - feedback to GOsa in success and error case
1033 sub opsi_get_local_products {
1034 my ($msg, $msg_hash, $session_id) = @_;
1035 my $header = @{$msg_hash->{'header'}}[0];
1036 my $source = @{$msg_hash->{'source'}}[0];
1037 my $target = @{$msg_hash->{'target'}}[0];
1038 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1039 my $hostId;
1041 # Build return message with twisted target and source
1042 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1043 if (defined $forward_to_gosa) {
1044 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1045 }
1046 &add_content2xml_hash($out_hash, "xxx", "");
1048 # Get hostId if defined
1049 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
1050 $hostId = @{$msg_hash->{'hostId'}}[0];
1051 &add_content2xml_hash($out_hash, "hostId", $hostId);
1052 }
1054 # Move to XML string
1055 my $xml_msg= &create_xml_string($out_hash);
1057 # For hosts, only return the products that are or get installed
1058 my $callobj;
1059 $callobj = {
1060 method => 'getLocalBootProductIds_list',
1061 params => [ ],
1062 id => 1,
1063 };
1065 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1066 my %r = ();
1067 for (@{$res->result}) { $r{$_} = 1 }
1069 if (not &check_opsi_res($res)){
1071 if (defined $hostId){
1072 $callobj = {
1073 method => 'getProductStates_hash',
1074 params => [ $hostId ],
1075 id => 1,
1076 };
1078 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1079 if (not &check_opsi_res($hres)){
1080 my $htmp= $hres->result->{$hostId};
1082 # Check state != not_installed or action == setup -> load and add
1083 foreach my $product (@{$htmp}){
1085 if (!defined ($r{$product->{'productId'}})){
1086 next;
1087 }
1089 # Now we've a couple of hashes...
1090 if ($product->{'installationStatus'} ne "not_installed" or
1091 $product->{'actionRequest'} eq "setup"){
1092 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1094 $callobj = {
1095 method => 'getProduct_hash',
1096 params => [ $product->{'productId'} ],
1097 id => 1,
1098 };
1100 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1101 if (not &check_opsi_res($sres)){
1102 my $tres= $sres->result;
1104 my $name= xml_quote($tres->{'name'});
1105 my $r= $product->{'productId'};
1106 my $description= xml_quote($tres->{'description'});
1107 $name=~ s/\//\\\//;
1108 $description=~ s/\//\\\//;
1109 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1110 }
1112 }
1113 }
1115 }
1117 } else {
1118 foreach my $r (@{$res->result}) {
1119 $callobj = {
1120 method => 'getProduct_hash',
1121 params => [ $r ],
1122 id => 1,
1123 };
1125 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1126 if (not &check_opsi_res($sres)){
1127 my $tres= $sres->result;
1129 my $name= xml_quote($tres->{'name'});
1130 my $description= xml_quote($tres->{'description'});
1131 $name=~ s/\//\\\//;
1132 $description=~ s/\//\\\//;
1133 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1134 }
1136 }
1138 }
1139 }
1141 $xml_msg=~ s/<xxx><\/xxx>//;
1143 # Retrun Message
1144 return ( $xml_msg );
1145 }
1148 ## @method opsi_del_client
1149 # Deletes a client from Opsi.
1150 # @param msg - STRING - xml message with tag hostId
1151 # @param msg_hash - HASHREF - message information parsed into a hash
1152 # @param session_id - INTEGER - POE session id of the processing of this message
1153 # @return out_msg - STRING - feedback to GOsa in success and error case
1154 sub opsi_del_client {
1155 my ($msg, $msg_hash, $session_id) = @_;
1156 my $header = @{$msg_hash->{'header'}}[0];
1157 my $source = @{$msg_hash->{'source'}}[0];
1158 my $target = @{$msg_hash->{'target'}}[0];
1159 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1160 my $hostId;
1161 my $error = 0;
1163 # Build return message with twisted target and source
1164 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1165 if (defined $forward_to_gosa) {
1166 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1167 }
1169 # Sanity check of needed parameter
1170 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1171 $error++;
1172 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1173 &add_content2xml_hash($out_hash, "error", "hostId");
1174 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
1175 }
1177 if (not $error) {
1179 # Get hostId
1180 $hostId = @{$msg_hash->{'hostId'}}[0];
1181 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1183 # JSON Query
1184 my $callobj = {
1185 method => 'deleteClient',
1186 params => [ $hostId ],
1187 id => 1,
1188 };
1189 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1190 }
1192 # Move to XML string
1193 my $xml_msg= &create_xml_string($out_hash);
1195 # Return message
1196 return ( $xml_msg );
1197 }
1200 ## @method opsi_install_client
1201 # Set a client in Opsi to install and trigger a wake on lan message (WOL).
1202 # @param msg - STRING - xml message with tags hostId, macaddress
1203 # @param msg_hash - HASHREF - message information parsed into a hash
1204 # @param session_id - INTEGER - POE session id of the processing of this message
1205 # @return out_msg - STRING - feedback to GOsa in success and error case
1206 sub opsi_install_client {
1207 my ($msg, $msg_hash, $session_id) = @_;
1208 my $header = @{$msg_hash->{'header'}}[0];
1209 my $source = @{$msg_hash->{'source'}}[0];
1210 my $target = @{$msg_hash->{'target'}}[0];
1211 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1214 my ($hostId, $macaddress);
1216 my $error = 0;
1217 my @out_msg_l;
1219 # Build return message with twisted target and source
1220 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1221 if (defined $forward_to_gosa) {
1222 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1223 }
1225 # Sanity check of needed parameter
1226 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1227 $error++;
1228 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1229 &add_content2xml_hash($out_hash, "error", "hostId");
1230 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
1231 }
1232 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') ) {
1233 $error++;
1234 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1235 &add_content2xml_hash($out_hash, "error", "macaddress");
1236 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
1237 } else {
1238 if ((exists $msg_hash->{'macaddress'}) &&
1239 ($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)) {
1240 $macaddress = $1;
1241 } else {
1242 $error ++;
1243 &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1244 &add_content2xml_hash($out_hash, "error", "macaddress");
1245 &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1);
1246 }
1247 }
1249 if (not $error) {
1251 # Get hostId
1252 $hostId = @{$msg_hash->{'hostId'}}[0];
1253 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1255 # Load all products for this host with status != "not_installed" or actionRequest != "none"
1256 my $callobj = {
1257 method => 'getProductStates_hash',
1258 params => [ $hostId ],
1259 id => 1,
1260 };
1262 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1263 if (not &check_opsi_res($hres)){
1264 my $htmp= $hres->result->{$hostId};
1266 # check state != not_installed or action == setup -> load and add
1267 foreach my $product (@{$htmp}){
1268 # Now we've a couple of hashes...
1269 if ($product->{'installationStatus'} ne "not_installed" or
1270 $product->{'actionRequest'} ne "none"){
1272 # Do an action request for all these -> "setup".
1273 $callobj = {
1274 method => 'setProductActionRequest',
1275 params => [ $product->{'productId'}, $hostId, "setup" ],
1276 id => 1,
1277 };
1278 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1279 my ($res_err, $res_err_string) = &check_opsi_res($res);
1280 if ($res_err){
1281 &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1282 } else {
1283 &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1284 }
1285 }
1286 }
1287 }
1288 push(@out_msg_l, &create_xml_string($out_hash));
1291 # Build wakeup message for client
1292 if (not $error) {
1293 my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1294 &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1295 my $wakeup_msg = &create_xml_string($wakeup_hash);
1296 push(@out_msg_l, $wakeup_msg);
1298 # invoke trigger wake for this gosa-si-server
1299 &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1300 }
1301 }
1303 # Return messages
1304 return @out_msg_l;
1305 }
1308 ## @method _set_action
1309 # Set action for an Opsi client
1310 # @param product - STRING - Opsi product
1311 # @param action - STRING - action
1312 # @param hostId - STRING - Opsi hostId
1313 sub _set_action {
1314 my $product= shift;
1315 my $action = shift;
1316 my $hostId = shift;
1317 my $callobj;
1319 $callobj = {
1320 method => 'setProductActionRequest',
1321 params => [ $product, $hostId, $action],
1322 id => 1,
1323 };
1325 $main::opsi_client->call($main::opsi_url, $callobj);
1326 }
1328 ## @method _set_state
1329 # Set state for an Opsi client
1330 # @param product - STRING - Opsi product
1331 # @param action - STRING - state
1332 # @param hostId - STRING - Opsi hostId
1333 sub _set_state {
1334 my $product = shift;
1335 my $state = shift;
1336 my $hostId = shift;
1337 my $callobj;
1339 $callobj = {
1340 method => 'setProductState',
1341 params => [ $product, $hostId, $state ],
1342 id => 1,
1343 };
1345 $main::opsi_client->call($main::opsi_url, $callobj);
1346 }
1348 ################################
1349 #
1350 # @brief Create a license pool at Opsi server.
1351 # @param licensePoolId The name of the pool (optional).
1352 # @param description The description of the pool (optional).
1353 # @param productIds A list of assigned porducts of the pool (optional).
1354 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional).
1355 #
1356 sub opsi_createLicensePool {
1357 my ($msg, $msg_hash, $session_id) = @_;
1358 my $header = @{$msg_hash->{'header'}}[0];
1359 my $source = @{$msg_hash->{'source'}}[0];
1360 my $target = @{$msg_hash->{'target'}}[0];
1361 my $out_hash;
1362 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1363 my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1364 my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1365 my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1367 # Create license Pool
1368 my $callobj = {
1369 method => 'createLicensePool',
1370 params => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1371 id => 1,
1372 };
1373 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1375 # Check Opsi error
1376 my ($res_error, $res_error_str) = &check_opsi_res($res);
1377 if ($res_error){
1378 # Create error message
1379 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1380 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1381 return ( &create_xml_string($out_hash) );
1382 }
1384 # Create function result message
1385 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1387 return ( &create_xml_string($out_hash) );
1388 }
1390 ################################
1391 #
1392 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1393 #
1394 sub opsi_getLicensePools_listOfHashes {
1395 my ($msg, $msg_hash, $session_id) = @_;
1396 my $header = @{$msg_hash->{'header'}}[0];
1397 my $source = @{$msg_hash->{'source'}}[0];
1398 my $out_hash;
1400 # Fetch infos from Opsi server
1401 my $callobj = {
1402 method => 'getLicensePools_listOfHashes',
1403 params => [ ],
1404 id => 1,
1405 };
1406 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1408 # Check Opsi error
1409 my ($res_error, $res_error_str) = &check_opsi_res($res);
1410 if ($res_error){
1411 # Create error message
1412 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1413 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1414 return ( &create_xml_string($out_hash) );
1415 }
1417 # Create function result message
1418 my $res_hash = { 'hit'=> [] };
1419 foreach my $licensePool ( @{$res->result}) {
1420 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1421 'description' => [$licensePool->{'description'}],
1422 'productIds' => $licensePool->{'productIds'},
1423 'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1424 };
1425 push( @{$res_hash->{hit}}, $licensePool_hash );
1426 }
1428 # Create function result message
1429 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1430 $out_hash->{result} = [$res_hash];
1432 return ( &create_xml_string($out_hash) );
1433 }
1435 ################################
1436 #
1437 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1438 # @param licensePoolId The name of the pool.
1439 #
1440 sub opsi_getLicensePool_hash {
1441 my ($msg, $msg_hash, $session_id) = @_;
1442 my $header = @{$msg_hash->{'header'}}[0];
1443 my $source = @{$msg_hash->{'source'}}[0];
1444 my $target = @{$msg_hash->{'target'}}[0];
1445 my $licensePoolId;
1446 my $out_hash;
1448 # Check input sanity
1449 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1450 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1451 } else {
1452 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1453 }
1455 # Fetch infos from Opsi server
1456 my $callobj = {
1457 method => 'getLicensePool_hash',
1458 params => [ $licensePoolId ],
1459 id => 1,
1460 };
1461 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1463 # Check Opsi error
1464 my ($res_error, $res_error_str) = &check_opsi_res($res);
1465 if ($res_error){
1466 # Create error message
1467 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1468 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1469 &add_content2xml_hash($out_hash, "error", $res_error_str);
1470 return ( &create_xml_string($out_hash) );
1471 }
1473 # Create function result message
1474 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1475 &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1476 &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1477 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1478 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1480 return ( &create_xml_string($out_hash) );
1481 }
1483 ################################
1484 #
1485 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1486 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1487 # @param licensePoolId The name of the pool (optional).
1488 #
1489 sub opsi_getSoftwareLicenseUsages_listOfHashes {
1490 my ($msg, $msg_hash, $session_id) = @_;
1491 my $header = @{$msg_hash->{'header'}}[0];
1492 my $source = @{$msg_hash->{'source'}}[0];
1493 my $target = @{$msg_hash->{'target'}}[0];
1494 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1495 my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1496 my $out_hash;
1498 # Fetch information from Opsi server
1499 my $callobj = {
1500 method => 'getSoftwareLicenseUsages_listOfHashes',
1501 params => [ $hostId, $licensePoolId ],
1502 id => 1,
1503 };
1504 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1506 # Check Opsi error
1507 my ($res_error, $res_error_str) = &check_opsi_res($res);
1508 if ($res_error){
1509 # Create error message
1510 &main::daemon_log("$session_id ERROR: cannot fetch software licenses from license pool '$licensePoolId': ".$res_error_str, 1);
1511 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1512 return ( &create_xml_string($out_hash) );
1513 }
1515 # Parse Opsi result
1516 my $res_hash = { 'hit'=> [] };
1517 foreach my $license ( @{$res->result}) {
1518 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1519 'notes' => [$license->{'notes'}],
1520 'licenseKey' => [$license->{'licenseKey'}],
1521 'hostId' => [$license->{'hostId'}],
1522 'licensePoolId' => [$license->{'licensePoolId'}],
1523 };
1524 push( @{$res_hash->{hit}}, $license_hash );
1525 }
1527 # Create function result message
1528 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1529 $out_hash->{result} = [$res_hash];
1531 return ( &create_xml_string($out_hash) );
1532 }
1534 ################################
1535 #
1536 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1537 # @param softwareLicenseId Identificator of a license.
1538 #
1539 sub opsi_getSoftwareLicense_hash {
1540 my ($msg, $msg_hash, $session_id) = @_;
1541 my $header = @{$msg_hash->{'header'}}[0];
1542 my $source = @{$msg_hash->{'source'}}[0];
1543 my $target = @{$msg_hash->{'target'}}[0];
1544 my $softwareLicenseId;
1545 my $out_hash;
1547 # Check input sanity
1548 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1549 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1550 } else {
1551 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1552 }
1554 my $callobj = {
1555 method => 'getSoftwareLicense_hash',
1556 params => [ $softwareLicenseId ],
1557 id => 1,
1558 };
1559 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1561 # Check Opsi error
1562 my ($res_error, $res_error_str) = &check_opsi_res($res);
1563 if ($res_error){
1564 # Create error message
1565 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1566 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1567 return ( &create_xml_string($out_hash) );
1568 }
1570 # Create function result message
1571 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1572 &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1573 &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1574 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1575 &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1576 foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1577 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1578 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1579 }
1581 return ( &create_xml_string($out_hash) );
1582 }
1584 ################################
1585 #
1586 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool.
1587 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted.
1588 # @param licensePoolId The name of the pool.
1589 #
1590 sub opsi_deleteLicensePool {
1591 my ($msg, $msg_hash, $session_id) = @_;
1592 my $header = @{$msg_hash->{'header'}}[0];
1593 my $source = @{$msg_hash->{'source'}}[0];
1594 my $target = @{$msg_hash->{'target'}}[0];
1595 my $licensePoolId;
1596 my $out_hash;
1598 # Check input sanity
1599 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1600 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1601 } else {
1602 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1603 }
1605 # Fetch softwareLicenseIds used in license pool
1606 # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1607 # but not the license contracts of the software licenses. In our case each software license has exactly one license contract.
1608 my $callobj = {
1609 method => 'getSoftwareLicenses_listOfHashes',
1610 params => [ ],
1611 id => 1,
1612 };
1613 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1615 # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1616 my @lCI_toBeDeleted;
1617 foreach my $softwareLicenseHash ( @{$res->result} ) {
1618 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) {
1619 next;
1620 }
1621 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1622 }
1624 # Delete license pool at Opsi server
1625 $callobj = {
1626 method => 'deleteLicensePool',
1627 params => [ $licensePoolId, 'deleteLicenses=True' ],
1628 id => 1,
1629 };
1630 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1631 my ($res_error, $res_error_str) = &check_opsi_res($res);
1632 if ($res_error){
1633 # Create error message
1634 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1635 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1636 return ( &create_xml_string($out_hash) );
1637 }
1639 # Delete each license contract connected with the license pool
1640 foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1641 my $callobj = {
1642 method => 'deleteLicenseContract',
1643 params => [ $licenseContractId ],
1644 id => 1,
1645 };
1646 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1647 my ($res_error, $res_error_str) = &check_opsi_res($res);
1648 if ($res_error){
1649 # Create error message
1650 &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1651 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1652 return ( &create_xml_string($out_hash) );
1653 }
1654 }
1656 # Create function result message
1657 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1659 return ( &create_xml_string($out_hash) );
1660 }
1662 ################################
1663 #
1664 # @brief Create a license contract, create a software license and add the software license to the license pool
1665 # @param licensePoolId The name of the pool the license should be assigned.
1666 # @param licenseKey The license key.
1667 # @param partner Name of the license partner (optional).
1668 # @param conclusionDate Date of conclusion of license contract (optional)
1669 # @param notificationDate Date of notification that license is running out soon (optional).
1670 # @param notes This is the place for some notes (optional)
1671 # @param softwareLicenseId Identificator of a license (optional).
1672 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1673 # @param maxInstallations The number of clients use this license (optional).
1674 # @param boundToHost The name of the client the license is bound to (optional).
1675 # @param expirationDate The date when the license is running down (optional).
1676 #
1677 sub opsi_createLicense {
1678 my ($msg, $msg_hash, $session_id) = @_;
1679 my $header = @{$msg_hash->{'header'}}[0];
1680 my $source = @{$msg_hash->{'source'}}[0];
1681 my $target = @{$msg_hash->{'target'}}[0];
1682 my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1683 my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1684 my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1685 my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1686 my $licenseContractId;
1687 my $softwareLicenseId = defined $msg_hash->{'licenseId'} ? @{$msg_hash->{'licenseId'}}[0] : undef;
1688 my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1689 my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1690 my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1691 my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1692 my $licensePoolId;
1693 my $licenseKey;
1694 my $out_hash;
1696 # Check input sanity
1697 if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1698 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1699 } else {
1700 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1701 }
1702 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1703 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1704 } else {
1705 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1706 }
1707 if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1708 return ( &_give_feedback($msg, $msg_hash, $session_id, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'."));
1709 }
1711 # Create license contract at Opsi server
1712 my $callobj = {
1713 method => 'createLicenseContract',
1714 params => [ undef, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1715 id => 1,
1716 };
1717 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1719 # Check Opsi error
1720 my ($res_error, $res_error_str) = &check_opsi_res($res);
1721 if ($res_error){
1722 # Create error message
1723 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1724 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1725 return ( &create_xml_string($out_hash) );
1726 }
1728 $licenseContractId = $res->result;
1730 # Create software license at Opsi server
1731 $callobj = {
1732 method => 'createSoftwareLicense',
1733 params => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1734 id => 1,
1735 };
1736 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1738 # Check Opsi error
1739 ($res_error, $res_error_str) = &check_opsi_res($res);
1740 if ($res_error){
1741 # Create error message
1742 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1743 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1744 return ( &create_xml_string($out_hash) );
1745 }
1747 $softwareLicenseId = $res->result;
1749 # Add software license to license pool
1750 $callobj = {
1751 method => 'addSoftwareLicenseToLicensePool',
1752 params => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1753 id => 1,
1754 };
1755 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1757 # Check Opsi error
1758 ($res_error, $res_error_str) = &check_opsi_res($res);
1759 if ($res_error){
1760 # Create error message
1761 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1762 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1763 return ( &create_xml_string($out_hash) );
1764 }
1766 # Create function result message
1767 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1769 return ( &create_xml_string($out_hash) );
1770 }
1772 ################################
1773 #
1774 # @brief Assign a software license to a host
1775 # @param hostid Something like client_1.intranet.mydomain.de
1776 # @param licensePoolId The name of the pool.
1777 #
1778 sub opsi_assignSoftwareLicenseToHost {
1779 my ($msg, $msg_hash, $session_id) = @_;
1780 my $header = @{$msg_hash->{'header'}}[0];
1781 my $source = @{$msg_hash->{'source'}}[0];
1782 my $target = @{$msg_hash->{'target'}}[0];
1783 my $hostId;
1784 my $licensePoolId;
1786 # Check input sanity
1787 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1788 $hostId = @{$msg_hash->{'hostId'}}[0];
1789 } else {
1790 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1791 }
1792 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1793 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1794 } else {
1795 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1796 }
1798 # Assign a software license to a host
1799 my $callobj = {
1800 method => 'getAndAssignSoftwareLicenseKey',
1801 params => [ $hostId, $licensePoolId ],
1802 id => 1,
1803 };
1804 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1806 # Check Opsi error
1807 my ($res_error, $res_error_str) = &check_opsi_res($res);
1808 if ($res_error){
1809 # Create error message
1810 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1811 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1812 return ( &create_xml_string($out_hash) );
1813 }
1815 # Create function result message
1816 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1818 return ( &create_xml_string($out_hash) );
1819 }
1821 ################################
1822 #
1823 # @brief Unassign a software license from a host.
1824 # @param hostid Something like client_1.intranet.mydomain.de
1825 # @param licensePoolId The name of the pool.
1826 #
1827 sub opsi_unassignSoftwareLicenseFromHost {
1828 my ($msg, $msg_hash, $session_id) = @_;
1829 my $header = @{$msg_hash->{'header'}}[0];
1830 my $source = @{$msg_hash->{'source'}}[0];
1831 my $target = @{$msg_hash->{'target'}}[0];
1832 my $hostId;
1833 my $licensePoolId;
1835 # Check input sanity
1836 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1837 $hostId = @{$msg_hash->{'hostId'}}[0];
1838 } else {
1839 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1840 }
1841 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1842 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1843 } else {
1844 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1845 }
1847 # Unassign a software license from a host
1848 my $callobj = {
1849 method => 'deleteSoftwareLicenseUsage',
1850 params => [ $hostId, '', $licensePoolId ],
1851 id => 1,
1852 };
1853 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1855 # Check Opsi error
1856 my ($res_error, $res_error_str) = &check_opsi_res($res);
1857 if ($res_error){
1858 # Create error message
1859 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1860 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1861 return ( &create_xml_string($out_hash) );
1862 }
1864 # Create function result message
1865 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1867 return ( &create_xml_string($out_hash) );
1868 }
1870 ################################
1871 #
1872 # @brief Unassign all software licenses from a host
1873 # @param hostid Something like client_1.intranet.mydomain.de
1874 #
1875 sub opsi_unassignAllSoftwareLicensesFromHost {
1876 my ($msg, $msg_hash, $session_id) = @_;
1877 my $header = @{$msg_hash->{'header'}}[0];
1878 my $source = @{$msg_hash->{'source'}}[0];
1879 my $target = @{$msg_hash->{'target'}}[0];
1880 my $hostId;
1882 # Check input sanity
1883 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1884 $hostId = @{$msg_hash->{'hostId'}}[0];
1885 } else {
1886 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1887 }
1889 # Unassign all software licenses from a host
1890 my $callobj = {
1891 method => 'deleteAllSoftwareLicenseUsages',
1892 params => [ $hostId ],
1893 id => 1,
1894 };
1895 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1897 # Check Opsi error
1898 my ($res_error, $res_error_str) = &check_opsi_res($res);
1899 if ($res_error){
1900 # Create error message
1901 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1902 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1903 return ( &create_xml_string($out_hash) );
1904 }
1906 # Create function result message
1907 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1909 return ( &create_xml_string($out_hash) );
1910 }
1913 ################################
1914 #
1915 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1916 # and the number of max and remaining installations for a given OPSI product.
1917 # @param productId Identificator of an OPSI product.
1918 #
1919 sub opsi_getLicenseInformationForProduct {
1920 my ($msg, $msg_hash, $session_id) = @_;
1921 my $header = @{$msg_hash->{'header'}}[0];
1922 my $source = @{$msg_hash->{'source'}}[0];
1923 my $productId;
1924 my $out_hash;
1926 # Check input sanity
1927 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1928 $productId = @{$msg_hash->{'productId'}}[0];
1929 } else {
1930 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1931 }
1933 # Fetch infos from Opsi server
1934 my $callobj = {
1935 method => 'getLicensePoolId',
1936 params => [ $productId ],
1937 id => 1,
1938 };
1939 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1941 # Check Opsi error
1942 my ($res_error, $res_error_str) = &check_opsi_res($res);
1943 if ($res_error){
1944 # Create error message
1945 &main::daemon_log("$session_id ERROR: cannot get license pool for product '$productId' : ".$res_error_str, 1);
1946 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1947 return ( &create_xml_string($out_hash) );
1948 }
1950 my $licensePoolId = $res->result;
1952 # Fetch statistic information for given pool ID
1953 $callobj = {
1954 method => 'getLicenseStatistics_hash',
1955 params => [ ],
1956 id => 1,
1957 };
1958 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1960 # Check Opsi error
1961 ($res_error, $res_error_str) = &check_opsi_res($res);
1962 if ($res_error){
1963 # Create error message
1964 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
1965 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1966 return ( &create_xml_string($out_hash) );
1967 }
1969 # Create function result message
1970 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1971 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1972 &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
1973 &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
1974 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
1975 &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
1976 map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
1978 return ( &create_xml_string($out_hash) );
1979 }
1982 ################################
1983 #
1984 # @brief
1985 # @param
1986 #
1987 sub opsi_getPool {
1988 my ($msg, $msg_hash, $session_id) = @_;
1989 my $header = @{$msg_hash->{'header'}}[0];
1990 my $source = @{$msg_hash->{'source'}}[0];
1992 # Check input sanity
1993 my $licensePoolId;
1994 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1995 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1996 } else {
1997 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1998 }
2000 # Create hash for the answer
2001 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2003 # Call Opsi
2004 my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2005 if ($err){
2006 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2007 }
2008 # Add data to outgoing hash
2009 &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2010 &add_content2xml_hash($out_hash, "description", $res->{'description'});
2011 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2012 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2015 # Call Opsi two times
2016 my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2017 if ($usages_err){
2018 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
2019 }
2020 my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2021 if ($licenses_err){
2022 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2023 }
2025 # Add data to outgoing hash
2026 # Parse through all software licenses and select those associated to the pool
2027 my $res_hash = { 'hit'=> [] };
2028 foreach my $license ( @$licenses_res) {
2029 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2030 my $found = 0;
2031 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2032 foreach my $lPI ( @licensePoolIds_list) {
2033 if ($lPI eq $licensePoolId) { $found++ }
2034 }
2035 if (not $found ) { next; };
2036 # Found matching licensePoolId
2037 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2038 'licenseKeys' => {},
2039 'expirationDate' => [$license->{'expirationDate'}],
2040 'boundToHost' => [$license->{'boundToHost'}],
2041 'maxInstallations' => [$license->{'maxInstallations'}],
2042 'licenseType' => [$license->{'licenseType'}],
2043 'licenseContractId' => [$license->{'licenseContractId'}],
2044 'licensePoolIds' => [],
2045 'hostIds' => [],
2046 };
2047 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2048 push( @{$license_hash->{'licensePooIds'}}, $licensePoolId);
2049 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
2050 }
2051 foreach my $usage (@$usages_res) {
2052 # Search for hostIds with matching softwareLicenseId
2053 if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2054 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2055 }
2056 }
2058 push( @{$res_hash->{hit}}, $license_hash );
2059 }
2060 $out_hash->{licenses} = [$res_hash];
2062 return ( &create_xml_string($out_hash) );
2063 }
2066 ################################
2067 #
2068 # @brief Removes at first the software license from license pool and than deletes the software license.
2069 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2070 # @param softwareLicenseId
2071 # @param licensePoolId
2072 #
2073 sub opsi_removeLicense {
2074 my ($msg, $msg_hash, $session_id) = @_;
2075 my $header = @{$msg_hash->{'header'}}[0];
2076 my $source = @{$msg_hash->{'source'}}[0];
2078 # Check input sanity
2079 my $softwareLicenseId;
2080 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2081 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2082 } else {
2083 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2084 }
2085 my $licensePoolId;
2086 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2087 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2088 } else {
2089 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2090 }
2092 # Call Opsi
2093 my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2094 if ($err){
2095 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2096 }
2098 # Call Opsi
2099 ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2100 if ($err){
2101 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2102 }
2104 # Create hash for the answer
2105 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2106 return ( &create_xml_string($out_hash) );
2107 }
2109 sub opsi_test {
2110 my ($msg, $msg_hash, $session_id) = @_;
2111 my $header = @{$msg_hash->{'header'}}[0];
2112 my $source = @{$msg_hash->{'source'}}[0];
2113 my $pram1 = @{$msg_hash->{'productId'}}[0];
2115 print STDERR Dumper $pram1;
2117 # Fetch infos from Opsi server
2118 my $callobj = {
2119 method => 'getLicensePoolId',
2120 params => [ $pram1 ],
2121 id => 1,
2122 };
2123 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2125 print STDERR Dumper $res;
2126 return ();
2127 }
2129 sub _giveErrorFeedback {
2130 my ($msg_hash, $err_string, $session_id) = @_;
2131 &main::daemon_log("$session_id ERROR: $err_string", 1);
2132 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2133 return ( &create_xml_string($out_hash) );
2134 }
2137 sub _getLicensePool_hash {
2138 my %arg = (
2139 'licensePoolId' => undef,
2140 @_,
2141 );
2143 if (not defined $arg{licensePoolId} ) {
2144 return ("function requires licensePoolId as parameter", 1);
2145 }
2147 # Fetch pool infos from Opsi server
2148 my $callobj = {
2149 method => 'getLicensePool_hash',
2150 params => [ $arg{licensePoolId} ],
2151 id => 1,
2152 };
2153 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2155 # Check Opsi error
2156 my ($res_error, $res_error_str) = &check_opsi_res($res);
2157 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2159 return ($res->result, 0);
2160 }
2162 sub _getSoftwareLicenses_listOfHashes {
2163 # Fetch licenses associated to the given pool
2164 my $callobj = {
2165 method => 'getSoftwareLicenses_listOfHashes',
2166 params => [ ],
2167 id => 1,
2168 };
2169 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2171 # Check Opsi error
2172 my ($res_error, $res_error_str) = &check_opsi_res($res);
2173 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2175 return ($res->result, 0);
2176 }
2178 sub _getSoftwareLicenseUsages_listOfHashes {
2179 my %arg = (
2180 'hostId' => "",
2181 'licensePoolId' => "",
2182 @_,
2183 );
2185 # Fetch pool infos from Opsi server
2186 my $callobj = {
2187 method => 'getSoftwareLicenseUsages_listOfHashes',
2188 params => [ $arg{hostId}, $arg{licensePoolId} ],
2189 id => 1,
2190 };
2191 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2193 # Check Opsi error
2194 my ($res_error, $res_error_str) = &check_opsi_res($res);
2195 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2197 return ($res->result, 0);
2198 }
2200 sub _removeSoftwareLicenseFromLicensePool {
2201 my %arg = (
2202 'softwareLicenseId' => undef,
2203 'licensePoolId' => undef,
2204 @_,
2205 );
2207 if (not defined $arg{softwareLicenseId} ) {
2208 return ("function requires softwareLicenseId as parameter", 1);
2209 }
2210 if (not defined $arg{licensePoolId} ) {
2211 return ("function requires licensePoolId as parameter", 1);
2212 }
2214 # Remove software license from license pool
2215 my $callobj = {
2216 method => 'removeSoftwareLicenseFromLicensePool',
2217 params => [ $arg{softwareLicenseId}, $arg{licensePoolId} ],
2218 id => 1,
2219 };
2220 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2222 # Check Opsi error
2223 my ($res_error, $res_error_str) = &check_opsi_res($res);
2224 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2226 return ($res->result, 0);
2227 }
2229 sub _deleteSoftwareLicense {
2230 my %arg = (
2231 'softwareLicenseId' => undef,
2232 'removeFromPools' => "",
2233 @_,
2234 );
2236 if (not defined $arg{softwareLicenseId} ) {
2237 return ("function requires softwareLicenseId as parameter", 1);
2238 }
2240 # Fetch
2241 my $callobj = {
2242 method => 'deleteSoftwareLicense',
2243 params => [ $arg{softwareLicenseId}, $arg{removeFromPools} ],
2244 id => 1,
2245 };
2246 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2248 # Check Opsi error
2249 my ($res_error, $res_error_str) = &check_opsi_res($res);
2250 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2252 return ($res->result, 0);
2253 }
2255 1;