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_test",
37 );
38 @EXPORT = @events;
40 use strict;
41 use warnings;
42 use GOSA::GosaSupportDaemon;
43 use Data::Dumper;
44 use XML::Quote qw(:all);
46 BEGIN {}
48 END {}
50 # ----------------------------------------------------------------------------
51 # D E C L A R A T I O N S
52 # ----------------------------------------------------------------------------
54 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
58 # ----------------------------------------------------------------------------
59 # S U B R O U T I N E S
60 # ----------------------------------------------------------------------------
63 ################################
64 #
65 # @brief A function returning a list of functions which are exported by importing the module.
66 # @return List of all provided functions
67 #
68 sub get_events {
69 return \@events;
70 }
72 ################################
73 #
74 # @brief Checks if there is a specified tag and if the the tag has a content.
75 # @return 0|1
76 #
77 sub _check_xml_tag_is_ok {
78 my ($msg_hash,$tag) = @_;
79 if (not defined $msg_hash->{$tag}) {
80 $_ = "message contains no tag '$tag'";
81 return 0;
82 }
83 if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
84 $_ = "message tag '$tag' has no content";
85 return 0;
86 }
87 return 1;
88 }
90 ################################
91 #
92 # @brief Writes the log line and returns the error message for GOsa.
93 #
94 sub _give_feedback {
95 my ($msg, $msg_hash, $session_id, $error) = @_;
96 &main::daemon_log("$session_id ERROR: $error: ".$msg, 1);
97 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{'source'}}[0], $error);
98 return &create_xml_string($out_hash);
99 }
101 ## @method opsi_add_product_to_client
102 # Adds an Opsi product to an Opsi client.
103 # @param msg - STRING - xml message with tags hostId and productId
104 # @param msg_hash - HASHREF - message information parsed into a hash
105 # @param session_id - INTEGER - POE session id of the processing of this message
106 # @return out_msg - STRING - feedback to GOsa in success and error case
107 sub opsi_add_product_to_client {
108 my ($msg, $msg_hash, $session_id) = @_;
109 my $header = @{$msg_hash->{'header'}}[0];
110 my $source = @{$msg_hash->{'source'}}[0];
111 my $target = @{$msg_hash->{'target'}}[0];
112 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
113 my ($hostId, $productId);
114 my $error = 0;
116 # Build return message
117 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
118 if (defined $forward_to_gosa) {
119 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
120 }
122 # Sanity check of needed parameter
123 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
124 $error++;
125 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
126 &add_content2xml_hash($out_hash, "error", "hostId");
127 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
129 }
130 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
131 $error++;
132 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
133 &add_content2xml_hash($out_hash, "error", "productId");
134 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
135 }
137 if (not $error) {
138 # Get hostId
139 $hostId = @{$msg_hash->{'hostId'}}[0];
140 &add_content2xml_hash($out_hash, "hostId", $hostId);
142 # Get productID
143 $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 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
153 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
154 if ($sres_err){
155 &main::daemon_log("$session_id ERROR: cannot add product: ".$sres_err_string, 1);
156 &add_content2xml_hash($out_hash, "error", $sres_err_string);
157 }
158 }
160 # return message
161 return ( &create_xml_string($out_hash) );
162 }
164 ## @method opsi_del_product_from_client
165 # Deletes an Opsi-product from an Opsi-client.
166 # @param msg - STRING - xml message with tags hostId and productId
167 # @param msg_hash - HASHREF - message information parsed into a hash
168 # @param session_id - INTEGER - POE session id of the processing of this message
169 # @return out_msg - STRING - feedback to GOsa in success and error case
170 sub opsi_del_product_from_client {
171 my ($msg, $msg_hash, $session_id) = @_;
172 my $header = @{$msg_hash->{'header'}}[0];
173 my $source = @{$msg_hash->{'source'}}[0];
174 my $target = @{$msg_hash->{'target'}}[0];
175 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
176 my ($hostId, $productId);
177 my $error = 0;
178 my ($sres, $sres_err, $sres_err_string);
180 # Build return message
181 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
182 if (defined $forward_to_gosa) {
183 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
184 }
186 # Sanity check of needed parameter
187 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
188 $error++;
189 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
190 &add_content2xml_hash($out_hash, "error", "hostId");
191 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
193 }
194 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
195 $error++;
196 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
197 &add_content2xml_hash($out_hash, "error", "productId");
198 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
199 }
201 # All parameter available
202 if (not $error) {
203 # Get hostId
204 $hostId = @{$msg_hash->{'hostId'}}[0];
205 &add_content2xml_hash($out_hash, "hostId", $hostId);
207 # Get productID
208 $productId = @{$msg_hash->{'productId'}}[0];
209 &add_content2xml_hash($out_hash, "productId", $productId);
212 # : check the results for more than one entry which is currently installed
213 #$callobj = {
214 # method => 'getProductDependencies_listOfHashes',
215 # params => [ $productId ],
216 # id => 1, };
217 #
218 #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
219 #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
220 #if ($sres_err){
221 # &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
222 # &add_content2xml_hash($out_hash, "error", $sres_err_string);
223 # return ( &create_xml_string($out_hash) );
224 #}
227 # Check to get product action list
228 my $callobj = {
229 method => 'getPossibleProductActions_list',
230 params => [ $productId ],
231 id => 1, };
232 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
233 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
234 if ($sres_err){
235 &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
236 &add_content2xml_hash($out_hash, "error", $sres_err_string);
237 $error++;
238 }
239 }
241 # Check action uninstall of product
242 if (not $error) {
243 my $uninst_possible= 0;
244 foreach my $r (@{$sres->result}) {
245 if ($r eq 'uninstall') {
246 $uninst_possible= 1;
247 }
248 }
249 if (!$uninst_possible){
250 &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
251 &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
252 $error++;
253 }
254 }
256 # Set product state to "none"
257 # Do an action request for all these -> "setup".
258 if (not $error) {
259 my $callobj = {
260 method => 'setProductActionRequest',
261 params => [ $productId, $hostId, "none" ],
262 id => 1,
263 };
264 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
265 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
266 if ($sres_err){
267 &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
268 &add_content2xml_hash($out_hash, "error", $sres_err_string);
269 }
270 }
272 # Return message
273 return ( &create_xml_string($out_hash) );
274 }
276 ## @method opsi_add_client
277 # Adds an Opsi client to Opsi.
278 # @param msg - STRING - xml message with tags hostId and macaddress
279 # @param msg_hash - HASHREF - message information parsed into a hash
280 # @param session_id - INTEGER - POE session id of the processing of this message
281 # @return out_msg - STRING - feedback to GOsa in success and error case
282 sub opsi_add_client {
283 my ($msg, $msg_hash, $session_id) = @_;
284 my $header = @{$msg_hash->{'header'}}[0];
285 my $source = @{$msg_hash->{'source'}}[0];
286 my $target = @{$msg_hash->{'target'}}[0];
287 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
288 my ($hostId, $mac);
289 my $error = 0;
290 my ($sres, $sres_err, $sres_err_string);
292 # Build return message with twisted target and source
293 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
294 if (defined $forward_to_gosa) {
295 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
296 }
298 # Sanity check of needed parameter
299 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
300 $error++;
301 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
302 &add_content2xml_hash($out_hash, "error", "hostId");
303 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
304 }
305 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH')) {
306 $error++;
307 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
308 &add_content2xml_hash($out_hash, "error", "macaddress");
309 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
310 }
312 if (not $error) {
313 # Get hostId
314 $hostId = @{$msg_hash->{'hostId'}}[0];
315 &add_content2xml_hash($out_hash, "hostId", $hostId);
317 # Get macaddress
318 $mac = @{$msg_hash->{'macaddress'}}[0];
319 &add_content2xml_hash($out_hash, "macaddress", $mac);
321 my $name= $hostId;
322 $name=~ s/^([^.]+).*$/$1/;
323 my $domain= $hostId;
324 $domain=~ s/^[^.]+\.(.*)$/$1/;
325 my ($description, $notes, $ip);
327 if (defined @{$msg_hash->{'description'}}[0]){
328 $description = @{$msg_hash->{'description'}}[0];
329 }
330 if (defined @{$msg_hash->{'notes'}}[0]){
331 $notes = @{$msg_hash->{'notes'}}[0];
332 }
333 if (defined @{$msg_hash->{'ip'}}[0]){
334 $ip = @{$msg_hash->{'ip'}}[0];
335 }
337 my $callobj;
338 $callobj = {
339 method => 'createClient',
340 params => [ $name, $domain, $description, $notes, $ip, $mac ],
341 id => 1,
342 };
344 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
345 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
346 if ($sres_err){
347 &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
348 &add_content2xml_hash($out_hash, "error", $sres_err_string);
349 } else {
350 &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5);
351 }
352 }
354 # Return message
355 return ( &create_xml_string($out_hash) );
356 }
358 ## @method opsi_modify_client
359 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
360 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
361 # @param msg_hash - HASHREF - message information parsed into a hash
362 # @param session_id - INTEGER - POE session id of the processing of this message
363 # @return out_msg - STRING - feedback to GOsa in success and error case
364 sub opsi_modify_client {
365 my ($msg, $msg_hash, $session_id) = @_;
366 my $header = @{$msg_hash->{'header'}}[0];
367 my $source = @{$msg_hash->{'source'}}[0];
368 my $target = @{$msg_hash->{'target'}}[0];
369 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
370 my $hostId;
371 my $error = 0;
372 my ($sres, $sres_err, $sres_err_string);
374 # Build return message with twisted target and source
375 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
376 if (defined $forward_to_gosa) {
377 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
378 }
380 # Sanity check of needed parameter
381 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
382 $error++;
383 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
384 &add_content2xml_hash($out_hash, "error", "hostId");
385 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
386 }
388 if (not $error) {
389 # Get hostId
390 $hostId = @{$msg_hash->{'hostId'}}[0];
391 &add_content2xml_hash($out_hash, "hostId", $hostId);
392 my $name= $hostId;
393 $name=~ s/^([^.]+).*$/$1/;
394 my $domain= $hostId;
395 $domain=~ s/^[^.]+(.*)$/$1/;
397 # Modify description, notes or mac if defined
398 my ($description, $notes, $mac);
399 my $callobj;
400 if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
401 $description = @{$msg_hash->{'description'}}[0];
402 $callobj = {
403 method => 'setHostDescription',
404 params => [ $hostId, $description ],
405 id => 1,
406 };
407 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
408 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
409 if ($sres_err){
410 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
411 &add_content2xml_hash($out_hash, "error", $sres_err_string);
412 }
413 }
414 if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
415 $notes = @{$msg_hash->{'notes'}}[0];
416 $callobj = {
417 method => 'setHostNotes',
418 params => [ $hostId, $notes ],
419 id => 1,
420 };
421 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
422 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
423 if ($sres_err){
424 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
425 &add_content2xml_hash($out_hash, "error", $sres_err_string);
426 }
427 }
428 if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
429 $mac = @{$msg_hash->{'mac'}}[0];
430 $callobj = {
431 method => 'setMacAddress',
432 params => [ $hostId, $mac ],
433 id => 1,
434 };
435 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
436 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
437 if ($sres_err){
438 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
439 &add_content2xml_hash($out_hash, "error", $sres_err_string);
440 }
441 }
442 }
444 # Return message
445 return ( &create_xml_string($out_hash) );
446 }
449 ## @method opsi_get_netboot_products
450 # Get netboot products for specific host.
451 # @param msg - STRING - xml message with tag hostId
452 # @param msg_hash - HASHREF - message information parsed into a hash
453 # @param session_id - INTEGER - POE session id of the processing of this message
454 # @return out_msg - STRING - feedback to GOsa in success and error case
455 sub opsi_get_netboot_products {
456 my ($msg, $msg_hash, $session_id) = @_;
457 my $header = @{$msg_hash->{'header'}}[0];
458 my $source = @{$msg_hash->{'source'}}[0];
459 my $target = @{$msg_hash->{'target'}}[0];
460 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
461 my $hostId;
462 my $xml_msg;
464 # Build return message with twisted target and source
465 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
466 if (defined $forward_to_gosa) {
467 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
468 }
470 # Get hostId if defined
471 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
472 $hostId = @{$msg_hash->{'hostId'}}[0];
473 &add_content2xml_hash($out_hash, "hostId", $hostId);
474 }
476 &add_content2xml_hash($out_hash, "xxx", "");
477 $xml_msg = &create_xml_string($out_hash);
478 # For hosts, only return the products that are or get installed
479 my $callobj;
480 $callobj = {
481 method => 'getNetBootProductIds_list',
482 params => [ ],
483 id => 1,
484 };
485 &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
486 &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
487 &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
488 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
489 &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
490 my %r = ();
491 for (@{$res->result}) { $r{$_} = 1 }
493 if (not &check_opsi_res($res)){
495 if (defined $hostId){
497 $callobj = {
498 method => 'getProductStates_hash',
499 params => [ $hostId ],
500 id => 1,
501 };
503 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
504 if (not &check_opsi_res($hres)){
505 my $htmp= $hres->result->{$hostId};
507 # check state != not_installed or action == setup -> load and add
508 foreach my $product (@{$htmp}){
510 if (!defined ($r{$product->{'productId'}})){
511 next;
512 }
514 # Now we've a couple of hashes...
515 if ($product->{'installationStatus'} ne "not_installed" or
516 $product->{'actionRequest'} eq "setup"){
517 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
519 $callobj = {
520 method => 'getProduct_hash',
521 params => [ $product->{'productId'} ],
522 id => 1,
523 };
525 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
526 if (not &check_opsi_res($sres)){
527 my $tres= $sres->result;
529 my $name= xml_quote($tres->{'name'});
530 my $r= $product->{'productId'};
531 my $description= xml_quote($tres->{'description'});
532 $name=~ s/\//\\\//;
533 $description=~ s/\//\\\//;
534 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
535 }
536 }
537 }
539 }
541 } else {
542 foreach my $r (@{$res->result}) {
543 $callobj = {
544 method => 'getProduct_hash',
545 params => [ $r ],
546 id => 1,
547 };
549 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
550 if (not &check_opsi_res($sres)){
551 my $tres= $sres->result;
553 my $name= xml_quote($tres->{'name'});
554 my $description= xml_quote($tres->{'description'});
555 $name=~ s/\//\\\//;
556 $description=~ s/\//\\\//;
557 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
558 }
559 }
561 }
562 }
563 $xml_msg=~ s/<xxx><\/xxx>//;
565 # Return message
566 return ( $xml_msg );
567 }
570 ## @method opsi_get_product_properties
571 # Get product properties for a product and a specific host or gobally for a product.
572 # @param msg - STRING - xml message with tags productId and optional hostId
573 # @param msg_hash - HASHREF - message information parsed into a hash
574 # @param session_id - INTEGER - POE session id of the processing of this message
575 # @return out_msg - STRING - feedback to GOsa in success and error case
576 sub opsi_get_product_properties {
577 my ($msg, $msg_hash, $session_id) = @_;
578 my $header = @{$msg_hash->{'header'}}[0];
579 my $source = @{$msg_hash->{'source'}}[0];
580 my $target = @{$msg_hash->{'target'}}[0];
581 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
582 my ($hostId, $productId);
583 my $xml_msg;
585 # Build return message with twisted target and source
586 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
587 if (defined $forward_to_gosa) {
588 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
589 }
591 # Sanity check of needed parameter
592 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
593 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
594 &add_content2xml_hash($out_hash, "error", "productId");
595 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
597 # Return message
598 return ( &create_xml_string($out_hash) );
599 }
601 # Get productid
602 $productId = @{$msg_hash->{'productId'}}[0];
603 &add_content2xml_hash($out_hash, "producId", "$productId");
605 # Get hostId if defined
606 if (defined @{$msg_hash->{'hostId'}}[0]){
607 $hostId = @{$msg_hash->{'hostId'}}[0];
608 &add_content2xml_hash($out_hash, "hostId", $hostId);
609 }
611 # Load actions
612 my $callobj = {
613 method => 'getPossibleProductActions_list',
614 params => [ $productId ],
615 id => 1,
616 };
617 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
618 if (not &check_opsi_res($res)){
619 foreach my $action (@{$res->result}){
620 &add_content2xml_hash($out_hash, "action", $action);
621 }
622 }
624 # Add place holder
625 &add_content2xml_hash($out_hash, "xxx", "");
627 # Move to XML string
628 $xml_msg= &create_xml_string($out_hash);
630 # JSON Query
631 if (defined $hostId){
632 $callobj = {
633 method => 'getProductProperties_hash',
634 params => [ $productId, $hostId ],
635 id => 1,
636 };
637 } else {
638 $callobj = {
639 method => 'getProductProperties_hash',
640 params => [ $productId ],
641 id => 1,
642 };
643 }
644 $res = $main::opsi_client->call($main::opsi_url, $callobj);
646 # JSON Query 2
647 $callobj = {
648 method => 'getProductPropertyDefinitions_listOfHashes',
649 params => [ $productId ],
650 id => 1,
651 };
653 # Assemble options
654 my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
655 my $values = {};
656 my $descriptions = {};
657 if (not &check_opsi_res($res2)){
658 my $r= $res2->result;
660 foreach my $entr (@$r){
661 # Unroll values
662 my $cnv;
663 if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
664 foreach my $v (@{$entr->{'values'}}){
665 $cnv.= "<value>$v</value>";
666 }
667 } else {
668 $cnv= $entr->{'values'};
669 }
670 $values->{$entr->{'name'}}= $cnv;
671 $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
672 }
673 }
675 if (not &check_opsi_res($res)){
676 my $r= $res->result;
677 foreach my $key (keys %{$r}) {
678 my $item= "\n<item>";
679 my $value= $r->{$key};
680 my $dsc= "";
681 my $vals= "";
682 if (defined $descriptions->{$key}){
683 $dsc= $descriptions->{$key};
684 }
685 if (defined $values->{$key}){
686 $vals= $values->{$key};
687 }
688 $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
689 $item.= "</item>";
690 $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
691 }
692 }
694 $xml_msg=~ s/<xxx><\/xxx>//;
696 # Return message
697 return ( $xml_msg );
698 }
701 ## @method opsi_set_product_properties
702 # 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.
703 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
704 # @param msg_hash - HASHREF - message information parsed into a hash
705 # @param session_id - INTEGER - POE session id of the processing of this message
706 # @return out_msg - STRING - feedback to GOsa in success and error case
707 sub opsi_set_product_properties {
708 my ($msg, $msg_hash, $session_id) = @_;
709 my $header = @{$msg_hash->{'header'}}[0];
710 my $source = @{$msg_hash->{'source'}}[0];
711 my $target = @{$msg_hash->{'target'}}[0];
712 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
713 my ($productId, $hostId);
715 # Build return message with twisted target and source
716 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
717 if (defined $forward_to_gosa) {
718 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
719 }
721 # Sanity check of needed parameter
722 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
723 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
724 &add_content2xml_hash($out_hash, "error", "productId");
725 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
726 return ( &create_xml_string($out_hash) );
727 }
728 if (not exists $msg_hash->{'item'}) {
729 &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
730 &add_content2xml_hash($out_hash, "error", "item");
731 &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1);
732 return ( &create_xml_string($out_hash) );
733 } else {
734 if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
735 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
736 &add_content2xml_hash($out_hash, "error", "name");
737 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1);
738 return ( &create_xml_string($out_hash) );
739 }
740 if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
741 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
742 &add_content2xml_hash($out_hash, "error", "value");
743 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1);
744 return ( &create_xml_string($out_hash) );
745 }
746 }
747 # if no hostId is given, set_product_properties will act on globally
748 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1)) {
749 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
750 &add_content2xml_hash($out_hash, "error", "hostId");
751 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
752 return ( &create_xml_string($out_hash) );
753 }
756 # Get productId
757 $productId = @{$msg_hash->{'productId'}}[0];
758 &add_content2xml_hash($out_hash, "productId", $productId);
760 # Get hostId if defined
761 if (exists $msg_hash->{'hostId'}){
762 $hostId = @{$msg_hash->{'hostId'}}[0];
763 &add_content2xml_hash($out_hash, "hostId", $hostId);
764 }
766 # Set product states if requested
767 if (defined @{$msg_hash->{'action'}}[0]){
768 &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
769 }
770 if (defined @{$msg_hash->{'state'}}[0]){
771 &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
772 }
774 # Find properties
775 foreach my $item (@{$msg_hash->{'item'}}){
776 # JSON Query
777 my $callobj;
779 if (defined $hostId){
780 $callobj = {
781 method => 'setProductProperty',
782 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
783 id => 1,
784 };
785 } else {
786 $callobj = {
787 method => 'setProductProperty',
788 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
789 id => 1,
790 };
791 }
793 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
794 my ($res_err, $res_err_string) = &check_opsi_res($res);
796 if ($res_err){
797 &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
798 &add_content2xml_hash($out_hash, "error", $res_err_string);
799 }
800 }
803 # Return message
804 return ( &create_xml_string($out_hash) );
805 }
808 ## @method opsi_get_client_hardware
809 # Reports client hardware inventory.
810 # @param msg - STRING - xml message with tag hostId
811 # @param msg_hash - HASHREF - message information parsed into a hash
812 # @param session_id - INTEGER - POE session id of the processing of this message
813 # @return out_msg - STRING - feedback to GOsa in success and error case
814 sub opsi_get_client_hardware {
815 my ($msg, $msg_hash, $session_id) = @_;
816 my $header = @{$msg_hash->{'header'}}[0];
817 my $source = @{$msg_hash->{'source'}}[0];
818 my $target = @{$msg_hash->{'target'}}[0];
819 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
820 my $hostId;
821 my $error = 0;
822 my $xml_msg;
824 # Build return message with twisted target and source
825 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
826 if (defined $forward_to_gosa) {
827 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
828 }
830 # Sanity check of needed parameter
831 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
832 $error++;
833 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
834 &add_content2xml_hash($out_hash, "error", "hostId");
835 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
836 }
838 if (not $error) {
840 # Get hostId
841 $hostId = @{$msg_hash->{'hostId'}}[0];
842 &add_content2xml_hash($out_hash, "hostId", "$hostId");
843 &add_content2xml_hash($out_hash, "xxx", "");
844 }
846 # Move to XML string
847 $xml_msg= &create_xml_string($out_hash);
849 if (not $error) {
851 # JSON Query
852 my $callobj = {
853 method => 'getHardwareInformation_hash',
854 params => [ $hostId ],
855 id => 1,
856 };
858 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
859 if (not &check_opsi_res($res)){
860 my $result= $res->result;
861 if (ref $result eq "HASH") {
862 foreach my $r (keys %{$result}){
863 my $item= "\n<item><id>".xml_quote($r)."</id>";
864 my $value= $result->{$r};
865 foreach my $sres (@{$value}){
867 foreach my $dres (keys %{$sres}){
868 if (defined $sres->{$dres}){
869 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
870 }
871 }
873 }
874 $item.= "</item>";
875 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
877 }
878 }
879 }
881 $xml_msg=~ s/<xxx><\/xxx>//;
883 }
885 # Return message
886 return ( $xml_msg );
887 }
890 ## @method opsi_list_clients
891 # Reports all Opsi clients.
892 # @param msg - STRING - xml message
893 # @param msg_hash - HASHREF - message information parsed into a hash
894 # @param session_id - INTEGER - POE session id of the processing of this message
895 # @return out_msg - STRING - feedback to GOsa in success and error case
896 sub opsi_list_clients {
897 my ($msg, $msg_hash, $session_id) = @_;
898 my $header = @{$msg_hash->{'header'}}[0];
899 my $source = @{$msg_hash->{'source'}}[0];
900 my $target = @{$msg_hash->{'target'}}[0];
901 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
903 # Build return message with twisted target and source
904 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
905 if (defined $forward_to_gosa) {
906 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
907 }
908 &add_content2xml_hash($out_hash, "xxx", "");
910 # Move to XML string
911 my $xml_msg= &create_xml_string($out_hash);
913 # JSON Query
914 my $callobj = {
915 method => 'getClients_listOfHashes',
916 params => [ ],
917 id => 1,
918 };
919 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
920 if (not &check_opsi_res($res)){
921 foreach my $host (@{$res->result}){
922 my $item= "\n<item><name>".$host->{'hostId'}."</name>";
923 if (defined($host->{'description'})){
924 $item.= "<description>".xml_quote($host->{'description'})."</description>";
925 }
926 if (defined($host->{'notes'})){
927 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
928 }
929 if (defined($host->{'lastSeen'})){
930 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
931 }
933 $callobj = {
934 method => 'getIpAddress',
935 params => [ $host->{'hostId'} ],
936 id => 1,
937 };
938 my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
939 if ( not &check_opsi_res($sres)){
940 $item.= "<ip>".xml_quote($sres->result)."</ip>";
941 }
943 $callobj = {
944 method => 'getMacAddress',
945 params => [ $host->{'hostId'} ],
946 id => 1,
947 };
948 $sres= $main::opsi_client->call($main::opsi_url, $callobj);
949 if ( not &check_opsi_res($sres)){
950 $item.= "<mac>".xml_quote($sres->result)."</mac>";
951 }
952 $item.= "</item>";
953 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
954 }
955 }
957 $xml_msg=~ s/<xxx><\/xxx>//;
958 return ( $xml_msg );
959 }
963 ## @method opsi_get_client_software
964 # Reports client software inventory.
965 # @param msg - STRING - xml message with tag hostId
966 # @param msg_hash - HASHREF - message information parsed into a hash
967 # @param session_id - INTEGER - POE session id of the processing of this message
968 # @return out_msg - STRING - feedback to GOsa in success and error case
969 sub opsi_get_client_software {
970 my ($msg, $msg_hash, $session_id) = @_;
971 my $header = @{$msg_hash->{'header'}}[0];
972 my $source = @{$msg_hash->{'source'}}[0];
973 my $target = @{$msg_hash->{'target'}}[0];
974 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
975 my $error = 0;
976 my $hostId;
977 my $xml_msg;
979 # Build return message with twisted target and source
980 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
981 if (defined $forward_to_gosa) {
982 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
983 }
985 # Sanity check of needed parameter
986 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
987 $error++;
988 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
989 &add_content2xml_hash($out_hash, "error", "hostId");
990 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
991 }
993 if (not $error) {
995 # Get hostId
996 $hostId = @{$msg_hash->{'hostId'}}[0];
997 &add_content2xml_hash($out_hash, "hostId", "$hostId");
998 &add_content2xml_hash($out_hash, "xxx", "");
999 }
1001 $xml_msg= &create_xml_string($out_hash);
1003 if (not $error) {
1005 # JSON Query
1006 my $callobj = {
1007 method => 'getSoftwareInformation_hash',
1008 params => [ $hostId ],
1009 id => 1,
1010 };
1012 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1013 if (not &check_opsi_res($res)){
1014 my $result= $res->result;
1015 }
1017 $xml_msg=~ s/<xxx><\/xxx>//;
1019 }
1021 # Return message
1022 return ( $xml_msg );
1023 }
1026 ## @method opsi_get_local_products
1027 # Reports product for given hostId or globally.
1028 # @param msg - STRING - xml message with optional tag hostId
1029 # @param msg_hash - HASHREF - message information parsed into a hash
1030 # @param session_id - INTEGER - POE session id of the processing of this message
1031 # @return out_msg - STRING - feedback to GOsa in success and error case
1032 sub opsi_get_local_products {
1033 my ($msg, $msg_hash, $session_id) = @_;
1034 my $header = @{$msg_hash->{'header'}}[0];
1035 my $source = @{$msg_hash->{'source'}}[0];
1036 my $target = @{$msg_hash->{'target'}}[0];
1037 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1038 my $hostId;
1040 # Build return message with twisted target and source
1041 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1042 if (defined $forward_to_gosa) {
1043 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1044 }
1045 &add_content2xml_hash($out_hash, "xxx", "");
1047 # Get hostId if defined
1048 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
1049 $hostId = @{$msg_hash->{'hostId'}}[0];
1050 &add_content2xml_hash($out_hash, "hostId", $hostId);
1051 }
1053 # Move to XML string
1054 my $xml_msg= &create_xml_string($out_hash);
1056 # For hosts, only return the products that are or get installed
1057 my $callobj;
1058 $callobj = {
1059 method => 'getLocalBootProductIds_list',
1060 params => [ ],
1061 id => 1,
1062 };
1064 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1065 my %r = ();
1066 for (@{$res->result}) { $r{$_} = 1 }
1068 if (not &check_opsi_res($res)){
1070 if (defined $hostId){
1071 $callobj = {
1072 method => 'getProductStates_hash',
1073 params => [ $hostId ],
1074 id => 1,
1075 };
1077 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1078 if (not &check_opsi_res($hres)){
1079 my $htmp= $hres->result->{$hostId};
1081 # Check state != not_installed or action == setup -> load and add
1082 foreach my $product (@{$htmp}){
1084 if (!defined ($r{$product->{'productId'}})){
1085 next;
1086 }
1088 # Now we've a couple of hashes...
1089 if ($product->{'installationStatus'} ne "not_installed" or
1090 $product->{'actionRequest'} eq "setup"){
1091 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1093 $callobj = {
1094 method => 'getProduct_hash',
1095 params => [ $product->{'productId'} ],
1096 id => 1,
1097 };
1099 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1100 if (not &check_opsi_res($sres)){
1101 my $tres= $sres->result;
1103 my $name= xml_quote($tres->{'name'});
1104 my $r= $product->{'productId'};
1105 my $description= xml_quote($tres->{'description'});
1106 $name=~ s/\//\\\//;
1107 $description=~ s/\//\\\//;
1108 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1109 }
1111 }
1112 }
1114 }
1116 } else {
1117 foreach my $r (@{$res->result}) {
1118 $callobj = {
1119 method => 'getProduct_hash',
1120 params => [ $r ],
1121 id => 1,
1122 };
1124 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1125 if (not &check_opsi_res($sres)){
1126 my $tres= $sres->result;
1128 my $name= xml_quote($tres->{'name'});
1129 my $description= xml_quote($tres->{'description'});
1130 $name=~ s/\//\\\//;
1131 $description=~ s/\//\\\//;
1132 $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1133 }
1135 }
1137 }
1138 }
1140 $xml_msg=~ s/<xxx><\/xxx>//;
1142 # Retrun Message
1143 return ( $xml_msg );
1144 }
1147 ## @method opsi_del_client
1148 # Deletes a client from Opsi.
1149 # @param msg - STRING - xml message with tag hostId
1150 # @param msg_hash - HASHREF - message information parsed into a hash
1151 # @param session_id - INTEGER - POE session id of the processing of this message
1152 # @return out_msg - STRING - feedback to GOsa in success and error case
1153 sub opsi_del_client {
1154 my ($msg, $msg_hash, $session_id) = @_;
1155 my $header = @{$msg_hash->{'header'}}[0];
1156 my $source = @{$msg_hash->{'source'}}[0];
1157 my $target = @{$msg_hash->{'target'}}[0];
1158 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1159 my $hostId;
1160 my $error = 0;
1162 # Build return message with twisted target and source
1163 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1164 if (defined $forward_to_gosa) {
1165 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1166 }
1168 # Sanity check of needed parameter
1169 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1170 $error++;
1171 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1172 &add_content2xml_hash($out_hash, "error", "hostId");
1173 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
1174 }
1176 if (not $error) {
1178 # Get hostId
1179 $hostId = @{$msg_hash->{'hostId'}}[0];
1180 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1182 # JSON Query
1183 my $callobj = {
1184 method => 'deleteClient',
1185 params => [ $hostId ],
1186 id => 1,
1187 };
1188 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1189 }
1191 # Move to XML string
1192 my $xml_msg= &create_xml_string($out_hash);
1194 # Return message
1195 return ( $xml_msg );
1196 }
1199 ## @method opsi_install_client
1200 # Set a client in Opsi to install and trigger a wake on lan message (WOL).
1201 # @param msg - STRING - xml message with tags hostId, macaddress
1202 # @param msg_hash - HASHREF - message information parsed into a hash
1203 # @param session_id - INTEGER - POE session id of the processing of this message
1204 # @return out_msg - STRING - feedback to GOsa in success and error case
1205 sub opsi_install_client {
1206 my ($msg, $msg_hash, $session_id) = @_;
1207 my $header = @{$msg_hash->{'header'}}[0];
1208 my $source = @{$msg_hash->{'source'}}[0];
1209 my $target = @{$msg_hash->{'target'}}[0];
1210 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1213 my ($hostId, $macaddress);
1215 my $error = 0;
1216 my @out_msg_l;
1218 # Build return message with twisted target and source
1219 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1220 if (defined $forward_to_gosa) {
1221 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1222 }
1224 # Sanity check of needed parameter
1225 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1226 $error++;
1227 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1228 &add_content2xml_hash($out_hash, "error", "hostId");
1229 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
1230 }
1231 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') ) {
1232 $error++;
1233 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1234 &add_content2xml_hash($out_hash, "error", "macaddress");
1235 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
1236 } else {
1237 if ((exists $msg_hash->{'macaddress'}) &&
1238 ($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)) {
1239 $macaddress = $1;
1240 } else {
1241 $error ++;
1242 &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1243 &add_content2xml_hash($out_hash, "error", "macaddress");
1244 &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1);
1245 }
1246 }
1248 if (not $error) {
1250 # Get hostId
1251 $hostId = @{$msg_hash->{'hostId'}}[0];
1252 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1254 # Load all products for this host with status != "not_installed" or actionRequest != "none"
1255 my $callobj = {
1256 method => 'getProductStates_hash',
1257 params => [ $hostId ],
1258 id => 1,
1259 };
1261 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1262 if (not &check_opsi_res($hres)){
1263 my $htmp= $hres->result->{$hostId};
1265 # check state != not_installed or action == setup -> load and add
1266 foreach my $product (@{$htmp}){
1267 # Now we've a couple of hashes...
1268 if ($product->{'installationStatus'} ne "not_installed" or
1269 $product->{'actionRequest'} ne "none"){
1271 # Do an action request for all these -> "setup".
1272 $callobj = {
1273 method => 'setProductActionRequest',
1274 params => [ $product->{'productId'}, $hostId, "setup" ],
1275 id => 1,
1276 };
1277 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1278 my ($res_err, $res_err_string) = &check_opsi_res($res);
1279 if ($res_err){
1280 &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1281 } else {
1282 &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1283 }
1284 }
1285 }
1286 }
1287 push(@out_msg_l, &create_xml_string($out_hash));
1290 # Build wakeup message for client
1291 if (not $error) {
1292 my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1293 &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1294 my $wakeup_msg = &create_xml_string($wakeup_hash);
1295 push(@out_msg_l, $wakeup_msg);
1297 # invoke trigger wake for this gosa-si-server
1298 &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1299 }
1300 }
1302 # Return messages
1303 return @out_msg_l;
1304 }
1307 ## @method _set_action
1308 # Set action for an Opsi client
1309 # @param product - STRING - Opsi product
1310 # @param action - STRING - action
1311 # @param hostId - STRING - Opsi hostId
1312 sub _set_action {
1313 my $product= shift;
1314 my $action = shift;
1315 my $hostId = shift;
1316 my $callobj;
1318 $callobj = {
1319 method => 'setProductActionRequest',
1320 params => [ $product, $hostId, $action],
1321 id => 1,
1322 };
1324 $main::opsi_client->call($main::opsi_url, $callobj);
1325 }
1327 ## @method _set_state
1328 # Set state for an Opsi client
1329 # @param product - STRING - Opsi product
1330 # @param action - STRING - state
1331 # @param hostId - STRING - Opsi hostId
1332 sub _set_state {
1333 my $product = shift;
1334 my $state = shift;
1335 my $hostId = shift;
1336 my $callobj;
1338 $callobj = {
1339 method => 'setProductState',
1340 params => [ $product, $hostId, $state ],
1341 id => 1,
1342 };
1344 $main::opsi_client->call($main::opsi_url, $callobj);
1345 }
1347 ################################
1348 #
1349 # @brief Create a license pool at Opsi server.
1350 # @param licensePoolId The name of the pool (optional).
1351 # @param description The description of the pool (optional).
1352 # @param productIds A list of assigned porducts of the pool (optional).
1353 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional).
1354 #
1355 sub opsi_createLicensePool {
1356 my ($msg, $msg_hash, $session_id) = @_;
1357 my $header = @{$msg_hash->{'header'}}[0];
1358 my $source = @{$msg_hash->{'source'}}[0];
1359 my $target = @{$msg_hash->{'target'}}[0];
1360 my $out_hash;
1361 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1362 my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1363 my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1364 my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1366 # Create license Pool
1367 my $callobj = {
1368 method => 'createLicensePool',
1369 params => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1370 id => 1,
1371 };
1372 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1374 # Check Opsi error
1375 my ($res_error, $res_error_str) = &check_opsi_res($res);
1376 if ($res_error){
1377 # Create error message
1378 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1379 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1380 return ( &create_xml_string($out_hash) );
1381 }
1383 # Create function result message
1384 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1386 return ( &create_xml_string($out_hash) );
1387 }
1389 ################################
1390 #
1391 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1392 #
1393 sub opsi_getLicensePools_listOfHashes {
1394 my ($msg, $msg_hash, $session_id) = @_;
1395 my $header = @{$msg_hash->{'header'}}[0];
1396 my $source = @{$msg_hash->{'source'}}[0];
1397 my $out_hash;
1399 # Fetch infos from Opsi server
1400 my $callobj = {
1401 method => 'getLicensePools_listOfHashes',
1402 params => [ ],
1403 id => 1,
1404 };
1405 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1407 # Check Opsi error
1408 my ($res_error, $res_error_str) = &check_opsi_res($res);
1409 if ($res_error){
1410 # Create error message
1411 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1412 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1413 return ( &create_xml_string($out_hash) );
1414 }
1416 # Create function result message
1417 my $res_hash = { 'hit'=> [] };
1418 foreach my $licensePool ( @{$res->result}) {
1419 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1420 'description' => [$licensePool->{'description'}],
1421 'productIds' => $licensePool->{'productIds'},
1422 'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1423 };
1424 push( @{$res_hash->{hit}}, $licensePool_hash );
1425 }
1427 # Create function result message
1428 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1429 $out_hash->{result} = [$res_hash];
1431 return ( &create_xml_string($out_hash) );
1432 }
1434 ################################
1435 #
1436 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1437 # @param licensePoolId The name of the pool.
1438 #
1439 sub opsi_getLicensePool_hash {
1440 my ($msg, $msg_hash, $session_id) = @_;
1441 my $header = @{$msg_hash->{'header'}}[0];
1442 my $source = @{$msg_hash->{'source'}}[0];
1443 my $target = @{$msg_hash->{'target'}}[0];
1444 my $licensePoolId;
1445 my $out_hash;
1447 # Check input sanity
1448 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1449 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1450 } else {
1451 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1452 }
1454 # Fetch infos from Opsi server
1455 my $callobj = {
1456 method => 'getLicensePool_hash',
1457 params => [ $licensePoolId ],
1458 id => 1,
1459 };
1460 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1462 # Check Opsi error
1463 my ($res_error, $res_error_str) = &check_opsi_res($res);
1464 if ($res_error){
1465 # Create error message
1466 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1467 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1468 &add_content2xml_hash($out_hash, "error", $res_error_str);
1469 return ( &create_xml_string($out_hash) );
1470 }
1472 # Create function result message
1473 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1474 &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1475 &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1476 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1477 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1479 return ( &create_xml_string($out_hash) );
1480 }
1482 ################################
1483 #
1484 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1485 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1486 # @param licensePoolId The name of the pool (optional).
1487 #
1488 sub opsi_getSoftwareLicenseUsages_listOfHashes {
1489 my ($msg, $msg_hash, $session_id) = @_;
1490 my $header = @{$msg_hash->{'header'}}[0];
1491 my $source = @{$msg_hash->{'source'}}[0];
1492 my $target = @{$msg_hash->{'target'}}[0];
1493 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1494 my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1495 my $out_hash;
1497 # Fetch information from Opsi server
1498 my $callobj = {
1499 method => 'getSoftwareLicenseUsages_listOfHashes',
1500 params => [ $hostId, $licensePoolId ],
1501 id => 1,
1502 };
1503 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1505 # Check Opsi error
1506 my ($res_error, $res_error_str) = &check_opsi_res($res);
1507 if ($res_error){
1508 # Create error message
1509 &main::daemon_log("$session_id ERROR: cannot fetch software licenses from license pool '$licensePoolId': ".$res_error_str, 1);
1510 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1511 return ( &create_xml_string($out_hash) );
1512 }
1514 # Parse Opsi result
1515 my $res_hash = { 'hit'=> [] };
1516 foreach my $license ( @{$res->result}) {
1517 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1518 'notes' => [$license->{'notes'}],
1519 'licenseKey' => [$license->{'licenseKey'}],
1520 'hostId' => [$license->{'hostId'}],
1521 'licensePoolId' => [$license->{'licensePoolId'}],
1522 };
1523 push( @{$res_hash->{hit}}, $license_hash );
1524 }
1526 # Create function result message
1527 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1528 $out_hash->{result} = [$res_hash];
1530 return ( &create_xml_string($out_hash) );
1531 }
1533 ################################
1534 #
1535 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1536 # @param softwareLicenseId Identificator of a license.
1537 #
1538 sub opsi_getSoftwareLicense_hash {
1539 my ($msg, $msg_hash, $session_id) = @_;
1540 my $header = @{$msg_hash->{'header'}}[0];
1541 my $source = @{$msg_hash->{'source'}}[0];
1542 my $target = @{$msg_hash->{'target'}}[0];
1543 my $softwareLicenseId;
1544 my $out_hash;
1546 # Check input sanity
1547 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1548 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1549 } else {
1550 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1551 }
1553 my $callobj = {
1554 method => 'getSoftwareLicense_hash',
1555 params => [ $softwareLicenseId ],
1556 id => 1,
1557 };
1558 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1560 # Check Opsi error
1561 my ($res_error, $res_error_str) = &check_opsi_res($res);
1562 if ($res_error){
1563 # Create error message
1564 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1565 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1566 return ( &create_xml_string($out_hash) );
1567 }
1569 # Create function result message
1570 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1571 &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1572 &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1573 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1574 &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1575 foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1576 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1577 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1578 }
1580 return ( &create_xml_string($out_hash) );
1581 }
1583 ################################
1584 #
1585 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool.
1586 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted.
1587 # @param licensePoolId The name of the pool.
1588 #
1589 sub opsi_deleteLicensePool {
1590 my ($msg, $msg_hash, $session_id) = @_;
1591 my $header = @{$msg_hash->{'header'}}[0];
1592 my $source = @{$msg_hash->{'source'}}[0];
1593 my $target = @{$msg_hash->{'target'}}[0];
1594 my $licensePoolId;
1595 my $out_hash;
1597 # Check input sanity
1598 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1599 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1600 } else {
1601 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1602 }
1604 # Fetch softwareLicenseIds used in license pool
1605 # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1606 # but not the license contracts of the software licenses. In our case each software license has exactly one license contract.
1607 my $callobj = {
1608 method => 'getSoftwareLicenses_listOfHashes',
1609 params => [ ],
1610 id => 1,
1611 };
1612 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1614 # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1615 my @lCI_toBeDeleted;
1616 foreach my $softwareLicenseHash ( @{$res->result} ) {
1617 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) {
1618 next;
1619 }
1620 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1621 }
1623 # Delete license pool at Opsi server
1624 $callobj = {
1625 method => 'deleteLicensePool',
1626 params => [ $licensePoolId, 'deleteLicenses=True' ],
1627 id => 1,
1628 };
1629 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1630 my ($res_error, $res_error_str) = &check_opsi_res($res);
1631 if ($res_error){
1632 # Create error message
1633 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1634 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1635 return ( &create_xml_string($out_hash) );
1636 }
1638 # Delete each license contract connected with the license pool
1639 foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1640 my $callobj = {
1641 method => 'deleteLicenseContract',
1642 params => [ $licenseContractId ],
1643 id => 1,
1644 };
1645 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1646 my ($res_error, $res_error_str) = &check_opsi_res($res);
1647 if ($res_error){
1648 # Create error message
1649 &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1650 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1651 return ( &create_xml_string($out_hash) );
1652 }
1653 }
1655 # Create function result message
1656 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1658 return ( &create_xml_string($out_hash) );
1659 }
1661 ################################
1662 #
1663 # @brief Create a license contract, create a software license and add the software license to the license pool
1664 # @param licensePoolId The name of the pool the license should be assigned.
1665 # @param licenseKey The license key.
1666 # @param partner Name of the license partner (optional).
1667 # @param conclusionDate Date of conclusion of license contract (optional)
1668 # @param notificationDate Date of notification that license is running out soon (optional).
1669 # @param notes This is the place for some notes (optional)
1670 # @param softwareLicenseId Identificator of a license (optional).
1671 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1672 # @param maxInstallations The number of clients use this license (optional).
1673 # @param boundToHost The name of the client the license is bound to (optional).
1674 # @param expirationDate The date when the license is running down (optional).
1675 #
1676 sub opsi_createLicense {
1677 my ($msg, $msg_hash, $session_id) = @_;
1678 my $header = @{$msg_hash->{'header'}}[0];
1679 my $source = @{$msg_hash->{'source'}}[0];
1680 my $target = @{$msg_hash->{'target'}}[0];
1681 my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1682 my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1683 my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1684 my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1685 my $licenseContractId;
1686 my $softwareLicenseId = defined $msg_hash->{'licenseId'} ? @{$msg_hash->{'licenseId'}}[0] : undef;
1687 my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1688 my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1689 my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1690 my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1691 my $licensePoolId;
1692 my $licenseKey;
1693 my $out_hash;
1695 # Check input sanity
1696 if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1697 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1698 } else {
1699 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1700 }
1701 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1702 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1703 } else {
1704 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1705 }
1706 if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1707 return ( &_give_feedback($msg, $msg_hash, $session_id, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'."));
1708 }
1710 # Create license contract at Opsi server
1711 my $callobj = {
1712 method => 'createLicenseContract',
1713 params => [ undef, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1714 id => 1,
1715 };
1716 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1718 # Check Opsi error
1719 my ($res_error, $res_error_str) = &check_opsi_res($res);
1720 if ($res_error){
1721 # Create error message
1722 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1723 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1724 return ( &create_xml_string($out_hash) );
1725 }
1727 $licenseContractId = $res->result;
1729 # Create software license at Opsi server
1730 $callobj = {
1731 method => 'createSoftwareLicense',
1732 params => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1733 id => 1,
1734 };
1735 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1737 # Check Opsi error
1738 ($res_error, $res_error_str) = &check_opsi_res($res);
1739 if ($res_error){
1740 # Create error message
1741 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1742 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1743 return ( &create_xml_string($out_hash) );
1744 }
1746 $softwareLicenseId = $res->result;
1748 # Add software license to license pool
1749 $callobj = {
1750 method => 'addSoftwareLicenseToLicensePool',
1751 params => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1752 id => 1,
1753 };
1754 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1756 # Check Opsi error
1757 ($res_error, $res_error_str) = &check_opsi_res($res);
1758 if ($res_error){
1759 # Create error message
1760 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1761 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1762 return ( &create_xml_string($out_hash) );
1763 }
1765 # Create function result message
1766 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1768 return ( &create_xml_string($out_hash) );
1769 }
1771 ################################
1772 #
1773 # @brief Assign a software license to a host
1774 # @param hostid Something like client_1.intranet.mydomain.de
1775 # @param licensePoolId The name of the pool.
1776 #
1777 sub opsi_assignSoftwareLicenseToHost {
1778 my ($msg, $msg_hash, $session_id) = @_;
1779 my $header = @{$msg_hash->{'header'}}[0];
1780 my $source = @{$msg_hash->{'source'}}[0];
1781 my $target = @{$msg_hash->{'target'}}[0];
1782 my $hostId;
1783 my $licensePoolId;
1785 # Check input sanity
1786 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1787 $hostId = @{$msg_hash->{'hostId'}}[0];
1788 } else {
1789 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1790 }
1791 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1792 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1793 } else {
1794 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1795 }
1797 # Assign a software license to a host
1798 my $callobj = {
1799 method => 'getAndAssignSoftwareLicenseKey',
1800 params => [ $hostId, $licensePoolId ],
1801 id => 1,
1802 };
1803 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1805 # Check Opsi error
1806 my ($res_error, $res_error_str) = &check_opsi_res($res);
1807 if ($res_error){
1808 # Create error message
1809 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1810 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1811 return ( &create_xml_string($out_hash) );
1812 }
1814 # Create function result message
1815 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1817 return ( &create_xml_string($out_hash) );
1818 }
1820 ################################
1821 #
1822 # @brief Unassign a software license from a host.
1823 # @param hostid Something like client_1.intranet.mydomain.de
1824 # @param licensePoolId The name of the pool.
1825 #
1826 sub opsi_unassignSoftwareLicenseFromHost {
1827 my ($msg, $msg_hash, $session_id) = @_;
1828 my $header = @{$msg_hash->{'header'}}[0];
1829 my $source = @{$msg_hash->{'source'}}[0];
1830 my $target = @{$msg_hash->{'target'}}[0];
1831 my $hostId;
1832 my $licensePoolId;
1834 # Check input sanity
1835 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1836 $hostId = @{$msg_hash->{'hostId'}}[0];
1837 } else {
1838 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1839 }
1840 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1841 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1842 } else {
1843 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1844 }
1846 # Unassign a software license from a host
1847 my $callobj = {
1848 method => 'deleteSoftwareLicenseUsage',
1849 params => [ $hostId, '', $licensePoolId ],
1850 id => 1,
1851 };
1852 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1854 # Check Opsi error
1855 my ($res_error, $res_error_str) = &check_opsi_res($res);
1856 if ($res_error){
1857 # Create error message
1858 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1859 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1860 return ( &create_xml_string($out_hash) );
1861 }
1863 # Create function result message
1864 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1866 return ( &create_xml_string($out_hash) );
1867 }
1869 ################################
1870 #
1871 # @brief Unassign all software licenses from a host
1872 # @param hostid Something like client_1.intranet.mydomain.de
1873 #
1874 sub opsi_unassignAllSoftwareLicensesFromHost {
1875 my ($msg, $msg_hash, $session_id) = @_;
1876 my $header = @{$msg_hash->{'header'}}[0];
1877 my $source = @{$msg_hash->{'source'}}[0];
1878 my $target = @{$msg_hash->{'target'}}[0];
1879 my $hostId;
1881 # Check input sanity
1882 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1883 $hostId = @{$msg_hash->{'hostId'}}[0];
1884 } else {
1885 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1886 }
1888 # Unassign all software licenses from a host
1889 my $callobj = {
1890 method => 'deleteAllSoftwareLicenseUsages',
1891 params => [ $hostId ],
1892 id => 1,
1893 };
1894 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1896 # Check Opsi error
1897 my ($res_error, $res_error_str) = &check_opsi_res($res);
1898 if ($res_error){
1899 # Create error message
1900 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1901 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1902 return ( &create_xml_string($out_hash) );
1903 }
1905 # Create function result message
1906 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1908 return ( &create_xml_string($out_hash) );
1909 }
1912 ################################
1913 #
1914 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1915 # and the number of max and remaining installations for a given OPSI product.
1916 # @param productId Identificator of an OPSI product.
1917 #
1918 sub opsi_getLicenseInformationForProduct {
1919 my ($msg, $msg_hash, $session_id) = @_;
1920 my $header = @{$msg_hash->{'header'}}[0];
1921 my $source = @{$msg_hash->{'source'}}[0];
1922 my $productId;
1923 my $out_hash;
1925 # Check input sanity
1926 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1927 $productId = @{$msg_hash->{'productId'}}[0];
1928 } else {
1929 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1930 }
1932 # Fetch infos from Opsi server
1933 my $callobj = {
1934 method => 'getLicensePoolId',
1935 params => [ $productId ],
1936 id => 1,
1937 };
1938 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1940 # Check Opsi error
1941 my ($res_error, $res_error_str) = &check_opsi_res($res);
1942 if ($res_error){
1943 # Create error message
1944 &main::daemon_log("$session_id ERROR: cannot get license pool for product '$productId' : ".$res_error_str, 1);
1945 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1946 return ( &create_xml_string($out_hash) );
1947 }
1949 my $licensePoolId = $res->result;
1951 # Fetch statistic information for given pool ID
1952 $callobj = {
1953 method => 'getLicenseStatistics_hash',
1954 params => [ ],
1955 id => 1,
1956 };
1957 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1959 # Check Opsi error
1960 ($res_error, $res_error_str) = &check_opsi_res($res);
1961 if ($res_error){
1962 # Create error message
1963 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
1964 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1965 return ( &create_xml_string($out_hash) );
1966 }
1968 # Create function result message
1969 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1970 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1971 &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
1972 &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
1973 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
1974 &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
1975 map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
1977 return ( &create_xml_string($out_hash) );
1978 }
1981 ################################
1982 #
1983 # @brief
1984 # @param
1985 #
1986 sub opsi_getPool {
1987 my ($msg, $msg_hash, $session_id) = @_;
1988 my $header = @{$msg_hash->{'header'}}[0];
1989 my $source = @{$msg_hash->{'source'}}[0];
1991 # Check input sanity
1992 my $licensePoolId;
1993 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1994 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1995 } else {
1996 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1997 }
1999 # Create hash for the answer
2000 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2002 # Call Opsi
2003 my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2004 if ($err){
2005 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2006 }
2007 # Add data to outgoing hash
2008 &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2009 &add_content2xml_hash($out_hash, "description", $res->{'description'});
2010 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2011 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2014 # Call Opsi two times
2015 my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2016 if ($usages_err){
2017 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$usages_res, $session_id);
2018 }
2019 my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2020 if ($licenses_err){
2021 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2022 }
2024 # Add data to outgoing hash
2025 # Parse through all software licenses and select those associated to the pool
2026 my $res_hash = { 'hit'=> [] };
2027 foreach my $license ( @$licenses_res) {
2028 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2029 my $found = 0;
2030 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2031 foreach my $lPI ( @licensePoolIds_list) {
2032 if ($lPI eq $licensePoolId) { $found++ }
2033 }
2034 if (not $found ) { next; };
2035 # Found matching licensePoolId
2036 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2037 'licenseKeys' => {},
2038 'expirationDate' => [$license->{'expirationDate'}],
2039 'boundToHost' => [$license->{'boundToHost'}],
2040 'maxInstallations' => [$license->{'maxInstallations'}],
2041 'licenseType' => [$license->{'licenseType'}],
2042 'licenseContractId' => [$license->{'licenseContractId'}],
2043 'licensePoolIds' => [],
2044 'hostIds' => [],
2045 };
2046 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2047 push( @{$license_hash->{'licensePooIds'}}, $licensePoolId);
2048 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
2049 }
2050 foreach my $usage (@$usages_res) {
2051 # Search for hostIds with matching softwareLicenseId
2052 if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2053 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2054 }
2055 }
2057 push( @{$res_hash->{hit}}, $license_hash );
2058 }
2059 $out_hash->{licenses} = [$res_hash];
2061 return ( &create_xml_string($out_hash) );
2062 }
2064 sub opsi_test {
2065 my ($msg, $msg_hash, $session_id) = @_;
2066 my $header = @{$msg_hash->{'header'}}[0];
2067 my $source = @{$msg_hash->{'source'}}[0];
2068 my $pram1 = @{$msg_hash->{'productId'}}[0];
2070 print STDERR Dumper $pram1;
2072 # Fetch infos from Opsi server
2073 my $callobj = {
2074 method => 'getLicensePoolId',
2075 params => [ $pram1 ],
2076 id => 1,
2077 };
2078 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2080 print STDERR Dumper $res;
2081 return ();
2082 }
2084 sub _giveErrorFeedback {
2085 my ($msg_hash, $err_string, $session_id) = @_;
2086 &main::daemon_log("$session_id ERROR: $err_string", 1);
2087 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2088 return ( &create_xml_string($out_hash) );
2089 }
2092 sub _getLicensePool_hash {
2093 my %arg = (
2094 'licensePoolId' => undef,
2095 @_,
2096 );
2098 if (not defined $arg{licensePoolId} ) {
2099 return ("function requires licensePoolId as parameter", 1);
2100 }
2102 # Fetch pool infos from Opsi server
2103 my $callobj = {
2104 method => 'getLicensePool_hash',
2105 params => [ $arg{licensePoolId} ],
2106 id => 1,
2107 };
2108 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2110 # Check Opsi error
2111 my ($res_error, $res_error_str) = &check_opsi_res($res);
2112 if ($res_error){
2113 return ( $res_error_str, 1 );
2114 }
2116 return ($res->result, 0);
2117 }
2119 sub _getSoftwareLicenses_listOfHashes {
2120 # Fetch licenses associated to the given pool
2121 my $callobj = {
2122 method => 'getSoftwareLicenses_listOfHashes',
2123 params => [ ],
2124 id => 1,
2125 };
2126 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2128 # Check Opsi error
2129 my ($res_error, $res_error_str) = &check_opsi_res($res);
2130 if ($res_error){
2131 return ( $res_error_str, 1 );
2132 }
2134 return ($res->result, 0);
2135 }
2137 sub _getSoftwareLicenseUsages_listOfHashes {
2138 my %arg = (
2139 'hostId' => "",
2140 'licensePoolId' => "",
2141 @_,
2142 );
2144 # Fetch pool infos from Opsi server
2145 my $callobj = {
2146 method => 'getSoftwareLicenseUsages_listOfHashes',
2147 params => [ $arg{hostId}, $arg{licensePoolId} ],
2148 id => 1,
2149 };
2150 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2152 # Check Opsi error
2153 my ($res_error, $res_error_str) = &check_opsi_res($res);
2154 if ($res_error){ return ( $res_error_str, 1 ); }
2156 return ($res->result, 0);
2157 }
2159 1;