98b21712c4042a1f4a169fc1471dfb2dd19334d1
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_get_full_product_host_information",
18 "opsi_set_product_properties",
19 "opsi_list_clients",
20 "opsi_del_client",
21 "opsi_add_client",
22 "opsi_modify_client",
23 "opsi_add_product_to_client",
24 "opsi_del_product_from_client",
25 "opsi_createLicensePool",
26 "opsi_deleteLicensePool",
27 "opsi_createLicense",
28 "opsi_assignSoftwareLicenseToHost",
29 "opsi_unassignSoftwareLicenseFromHost",
30 "opsi_unassignAllSoftwareLicensesFromHost",
31 "opsi_getSoftwareLicense_hash",
32 "opsi_getLicensePool_hash",
33 "opsi_getSoftwareLicenseUsages",
34 "opsi_getSoftwareLicenseUsagesForProductId",
35 "opsi_getLicensePools_listOfHashes",
36 "opsi_getLicenseInformationForProduct",
37 "opsi_getPool",
38 "opsi_getAllSoftwareLicenses",
39 "opsi_removeLicense",
40 "opsi_getReservedLicenses",
41 "opsi_boundHostToLicense",
42 "opsi_unboundHostFromLicense",
43 "opsi_test",
44 );
45 @EXPORT = @events;
47 use strict;
48 use warnings;
49 use GOSA::GosaSupportDaemon;
50 use Data::Dumper;
51 use XML::Quote qw(:all);
53 BEGIN {}
55 END {}
57 # ----------------------------------------------------------------------------
58 # D E C L A R A T I O N S
59 # ----------------------------------------------------------------------------
61 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
62 my ($opsi_enabled, $opsi_server, $opsi_admin, $opsi_password, $opsi_url, $opsi_client);
63 my %cfg_defaults = (
64 "Opsi" => {
65 "enabled" => [\$opsi_enabled, "false"],
66 "server" => [\$opsi_server, "localhost"],
67 "admin" => [\$opsi_admin, "opsi-admin"],
68 "password" => [\$opsi_password, "secret"],
69 },
70 );
71 &read_configfile($main::cfg_file, %cfg_defaults);
72 if ($opsi_enabled eq "true") {
73 use JSON::RPC::Client;
74 use XML::Quote qw(:all);
75 use Time::HiRes qw( time );
76 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
77 $opsi_client = new JSON::RPC::Client;
78 }
80 # ----------------------------------------------------------------------------
81 # external methods handling the comunication with GOsa/GOsa-si
82 # ----------------------------------------------------------------------------
84 ################################
85 # @brief A function returning a list of functions which are exported by importing the module.
86 # @return List of all provided functions
87 sub get_events {
88 return \@events;
89 }
91 ################################
92 # @brief Adds an Opsi product to an Opsi client.
93 # @param msg - STRING - xml message with tags hostId and productId
94 # @param msg_hash - HASHREF - message information parsed into a hash
95 # @param session_id - INTEGER - POE session id of the processing of this message
96 # @return out_msg - STRING - feedback to GOsa in success and error case
97 sub opsi_add_product_to_client {
98 my $startTime = Time::HiRes::time;
99 my ($msg, $msg_hash, $session_id) = @_;
100 my $header = @{$msg_hash->{'header'}}[0];
101 my $source = @{$msg_hash->{'source'}}[0];
102 my $target = @{$msg_hash->{'target'}}[0];
103 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
105 # Build return message
106 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
107 if (defined $forward_to_gosa) {
108 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
109 }
111 # Sanity check of needed parameter
112 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
113 return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
114 }
115 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
116 return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
117 }
119 # Get hostId
120 my $hostId = @{$msg_hash->{'hostId'}}[0];
121 &add_content2xml_hash($out_hash, "hostId", $hostId);
123 # Get productID
124 my $productId = @{$msg_hash->{'productId'}}[0];
125 &add_content2xml_hash($out_hash, "productId", $productId);
127 # Do an action request for all these -> "setup".
128 my $callobj = {
129 method => 'setProductActionRequest',
130 params => [ $productId, $hostId, "setup" ],
131 id => 1, };
132 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
134 if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
136 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
137 return ( &create_xml_string($out_hash) );
138 }
140 ################################
141 # @brief Deletes an Opsi-product from an Opsi-client.
142 # @param msg - STRING - xml message with tags hostId and productId
143 # @param msg_hash - HASHREF - message information parsed into a hash
144 # @param session_id - INTEGER - POE session id of the processing of this message
145 # @return out_msg - STRING - feedback to GOsa in success and error case
146 sub opsi_del_product_from_client {
147 my $startTime = Time::HiRes::time;
148 my ($msg, $msg_hash, $session_id) = @_;
149 my $header = @{$msg_hash->{'header'}}[0];
150 my $source = @{$msg_hash->{'source'}}[0];
151 my $target = @{$msg_hash->{'target'}}[0];
152 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
153 my ($hostId, $productId);
154 my $error = 0;
155 my ($sres, $sres_err, $sres_err_string);
157 # Build return message
158 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
159 if (defined $forward_to_gosa) {
160 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
161 }
163 # Sanity check of needed parameter
164 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
165 $error++;
166 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
167 &add_content2xml_hash($out_hash, "error", "hostId");
168 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
170 }
171 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
172 $error++;
173 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
174 &add_content2xml_hash($out_hash, "error", "productId");
175 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
176 }
178 # All parameter available
179 if (not $error) {
180 # Get hostId
181 $hostId = @{$msg_hash->{'hostId'}}[0];
182 &add_content2xml_hash($out_hash, "hostId", $hostId);
184 # Get productID
185 $productId = @{$msg_hash->{'productId'}}[0];
186 &add_content2xml_hash($out_hash, "productId", $productId);
188 # Check to get product action list
189 my $callobj = {
190 method => 'getPossibleProductActions_list',
191 params => [ $productId ],
192 id => 1, };
193 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
194 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
195 if ($sres_err){
196 &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
197 &add_content2xml_hash($out_hash, "error", $sres_err_string);
198 $error++;
199 }
200 }
202 # Check action uninstall of product
203 if (not $error) {
204 my $uninst_possible= 0;
205 foreach my $r (@{$sres->result}) {
206 if ($r eq 'uninstall') {
207 $uninst_possible= 1;
208 }
209 }
210 if (!$uninst_possible){
211 &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
212 &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
213 $error++;
214 }
215 }
217 # Set product state to "none"
218 # Do an action request for all these -> "setup".
219 if (not $error) {
220 my $callobj = {
221 method => 'setProductActionRequest',
222 params => [ $productId, $hostId, "none" ],
223 id => 1,
224 };
225 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
226 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
227 if ($sres_err){
228 &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
229 &add_content2xml_hash($out_hash, "error", $sres_err_string);
230 }
231 }
233 # Return message
234 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
235 return ( &create_xml_string($out_hash) );
236 }
238 ################################
239 # @brief Adds an Opsi client to Opsi.
240 # @param msg - STRING - xml message with tags hostId and macaddress
241 # @param msg_hash - HASHREF - message information parsed into a hash
242 # @param session_id - INTEGER - POE session id of the processing of this message
243 # @return out_msg - STRING - feedback to GOsa in success and error case
244 sub opsi_add_client {
245 my $startTime = Time::HiRes::time;
246 my ($msg, $msg_hash, $session_id) = @_;
247 my $header = @{$msg_hash->{'header'}}[0];
248 my $source = @{$msg_hash->{'source'}}[0];
249 my $target = @{$msg_hash->{'target'}}[0];
250 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
251 my ($hostId, $mac);
252 my $error = 0;
253 my ($sres, $sres_err, $sres_err_string);
255 # Build return message with twisted target and source
256 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
257 if (defined $forward_to_gosa) {
258 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
259 }
261 # Sanity check of needed parameter
262 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
263 $error++;
264 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
265 &add_content2xml_hash($out_hash, "error", "hostId");
266 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
267 }
268 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH')) {
269 $error++;
270 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
271 &add_content2xml_hash($out_hash, "error", "macaddress");
272 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
273 }
275 if (not $error) {
276 # Get hostId
277 $hostId = @{$msg_hash->{'hostId'}}[0];
278 &add_content2xml_hash($out_hash, "hostId", $hostId);
280 # Get macaddress
281 $mac = @{$msg_hash->{'macaddress'}}[0];
282 &add_content2xml_hash($out_hash, "macaddress", $mac);
284 my $name= $hostId;
285 $name=~ s/^([^.]+).*$/$1/;
286 my $domain= $hostId;
287 $domain=~ s/^[^.]+\.(.*)$/$1/;
288 my ($description, $notes, $ip);
290 if (defined @{$msg_hash->{'description'}}[0]){
291 $description = @{$msg_hash->{'description'}}[0];
292 }
293 if (defined @{$msg_hash->{'notes'}}[0]){
294 $notes = @{$msg_hash->{'notes'}}[0];
295 }
296 if (defined @{$msg_hash->{'ip'}}[0]){
297 $ip = @{$msg_hash->{'ip'}}[0];
298 }
300 my $callobj;
301 $callobj = {
302 method => 'createClient',
303 params => [ $name, $domain, $description, $notes, $ip, $mac ],
304 id => 1,
305 };
307 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
308 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
309 if ($sres_err){
310 &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
311 &add_content2xml_hash($out_hash, "error", $sres_err_string);
312 } else {
313 &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5);
314 }
315 }
317 # Return message
318 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
319 return ( &create_xml_string($out_hash) );
320 }
322 ################################
323 # @brief Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
324 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
325 # @param msg_hash - HASHREF - message information parsed into a hash
326 # @param session_id - INTEGER - POE session id of the processing of this message
327 # @return out_msg - STRING - feedback to GOsa in success and error case
328 sub opsi_modify_client {
329 my $startTime = Time::HiRes::time;
330 my ($msg, $msg_hash, $session_id) = @_;
331 my $header = @{$msg_hash->{'header'}}[0];
332 my $source = @{$msg_hash->{'source'}}[0];
333 my $target = @{$msg_hash->{'target'}}[0];
334 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
335 my $hostId;
336 my $error = 0;
337 my ($sres, $sres_err, $sres_err_string);
339 # Build return message with twisted target and source
340 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
341 if (defined $forward_to_gosa) {
342 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
343 }
345 # Sanity check of needed parameter
346 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
347 $error++;
348 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
349 &add_content2xml_hash($out_hash, "error", "hostId");
350 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
351 }
353 if (not $error) {
354 # Get hostId
355 $hostId = @{$msg_hash->{'hostId'}}[0];
356 &add_content2xml_hash($out_hash, "hostId", $hostId);
357 my $name= $hostId;
358 $name=~ s/^([^.]+).*$/$1/;
359 my $domain= $hostId;
360 $domain=~ s/^[^.]+(.*)$/$1/;
362 # Modify description, notes or mac if defined
363 my ($description, $notes, $mac);
364 my $callobj;
365 if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
366 $description = @{$msg_hash->{'description'}}[0];
367 $callobj = {
368 method => 'setHostDescription',
369 params => [ $hostId, $description ],
370 id => 1,
371 };
372 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
373 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
374 if ($sres_err){
375 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
376 &add_content2xml_hash($out_hash, "error", $sres_err_string);
377 }
378 }
379 if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
380 $notes = @{$msg_hash->{'notes'}}[0];
381 $callobj = {
382 method => 'setHostNotes',
383 params => [ $hostId, $notes ],
384 id => 1,
385 };
386 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
387 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
388 if ($sres_err){
389 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
390 &add_content2xml_hash($out_hash, "error", $sres_err_string);
391 }
392 }
393 if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
394 $mac = @{$msg_hash->{'mac'}}[0];
395 $callobj = {
396 method => 'setMacAddress',
397 params => [ $hostId, $mac ],
398 id => 1,
399 };
400 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
401 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
402 if ($sres_err){
403 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
404 &add_content2xml_hash($out_hash, "error", $sres_err_string);
405 }
406 }
407 }
409 # Return message
410 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
411 return ( &create_xml_string($out_hash) );
412 }
414 ################################
415 # @brief Get netboot products for specific host.
416 # @param msg - STRING - xml message with tag hostId
417 # @param msg_hash - HASHREF - message information parsed into a hash
418 # @param session_id - INTEGER - POE session id of the processing of this message
419 # @return out_msg - STRING - feedback to GOsa in success and error case
420 sub opsi_get_netboot_products {
421 my $startTime = Time::HiRes::time;
422 my ($msg, $msg_hash, $session_id) = @_;
423 my $header = @{$msg_hash->{'header'}}[0];
424 my $source = @{$msg_hash->{'source'}}[0];
425 my $target = @{$msg_hash->{'target'}}[0];
426 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
427 my $hostId;
429 # Build return message with twisted target and source
430 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
431 if (defined $forward_to_gosa) {
432 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
433 }
434 &add_content2xml_hash($out_hash, "xxx", "");
436 # Get hostId if defined
437 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
438 $hostId = @{$msg_hash->{'hostId'}}[0];
439 &add_content2xml_hash($out_hash, "hostId", $hostId);
440 }
442 # Move to XML string
443 my $xml_msg= &create_xml_string($out_hash);
445 my $callobj;
446 # Check if we need to get host or global information
447 if (defined $hostId){
448 $callobj = {
449 method => 'getProductHostInformation_list',
450 params => [ $hostId, undef, 'netboot'],
451 id => 1,
452 };
454 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
455 if (not &check_opsi_res($res)){
456 foreach my $product (@{$res->result}){
457 my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><state>".xml_quote($product->{'installationStatus'})."</state><action>".xml_quote($product->{'actionRequest'})."</action><\/item><xxx><\/xxx>";
458 $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
459 }
460 }
462 } else {
464 # For hosts, only return the products that are or get installed
465 $callobj = {
466 method => 'getProductInformation_list',
467 params => [ undef, 'netboot' ],
468 id => 1,
469 };
471 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
472 if (not &check_opsi_res($res)){
473 foreach my $product (@{$res->result}) {
474 my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
475 $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
476 }
477 }
478 }
480 $xml_msg=~ s/<xxx><\/xxx>//;
482 # Retrun Message
483 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
484 return ( $xml_msg );
485 }
487 ################################
488 # @brief Get product properties for a product and a specific host or gobally for a product.
489 # @param msg - STRING - xml message with tags productId and optional hostId
490 # @param msg_hash - HASHREF - message information parsed into a hash
491 # @param session_id - INTEGER - POE session id of the processing of this message
492 # @return out_msg - STRING - feedback to GOsa in success and error case
493 sub opsi_get_product_properties {
494 my $startTime = Time::HiRes::time;
495 my ($msg, $msg_hash, $session_id) = @_;
496 my $header = @{$msg_hash->{'header'}}[0];
497 my $source = @{$msg_hash->{'source'}}[0];
498 my $target = @{$msg_hash->{'target'}}[0];
499 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
500 my ($hostId, $productId);
501 my $xml_msg;
503 # Build return message with twisted target and source
504 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
505 if (defined $forward_to_gosa) {
506 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
507 }
509 # Sanity check of needed parameter
510 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
511 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
512 &add_content2xml_hash($out_hash, "error", "productId");
513 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
515 # Return message
516 return ( &create_xml_string($out_hash) );
517 }
519 # Get productid
520 $productId = @{$msg_hash->{'productId'}}[0];
521 &add_content2xml_hash($out_hash, "producId", "$productId");
523 # Get hostId if defined
524 if (defined @{$msg_hash->{'hostId'}}[0]){
525 $hostId = @{$msg_hash->{'hostId'}}[0];
526 &add_content2xml_hash($out_hash, "hostId", $hostId);
527 }
529 # Load actions
530 my $callobj = {
531 method => 'getPossibleProductActions_list',
532 params => [ $productId ],
533 id => 1,
534 };
535 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
536 if (not &check_opsi_res($res)){
537 foreach my $action (@{$res->result}){
538 &add_content2xml_hash($out_hash, "action", $action);
539 }
540 }
542 # Add place holder
543 &add_content2xml_hash($out_hash, "xxx", "");
545 # Move to XML string
546 $xml_msg= &create_xml_string($out_hash);
548 # JSON Query
549 if (defined $hostId){
550 $callobj = {
551 method => 'getProductProperties_hash',
552 params => [ $productId, $hostId ],
553 id => 1,
554 };
555 } else {
556 $callobj = {
557 method => 'getProductProperties_hash',
558 params => [ $productId ],
559 id => 1,
560 };
561 }
562 $res = $main::opsi_client->call($main::opsi_url, $callobj);
564 # JSON Query 2
565 $callobj = {
566 method => 'getProductPropertyDefinitions_listOfHashes',
567 params => [ $productId ],
568 id => 1,
569 };
571 # Assemble options
572 my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
573 my $values = {};
574 my $descriptions = {};
575 if (not &check_opsi_res($res2)){
576 my $r= $res2->result;
578 foreach my $entr (@$r){
579 # Unroll values
580 my $cnv;
581 if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
582 foreach my $v (@{$entr->{'values'}}){
583 $cnv.= "<value>$v</value>";
584 }
585 } else {
586 $cnv= $entr->{'values'};
587 }
588 $values->{$entr->{'name'}}= $cnv;
589 $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
590 }
591 }
593 if (not &check_opsi_res($res)){
594 my $r= $res->result;
595 foreach my $key (keys %{$r}) {
596 my $item= "\n<item>";
597 my $value= $r->{$key};
598 my $dsc= "";
599 my $vals= "";
600 if (defined $descriptions->{$key}){
601 $dsc= $descriptions->{$key};
602 }
603 if (defined $values->{$key}){
604 $vals= $values->{$key};
605 }
606 $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
607 $item.= "</item>";
608 $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
609 }
610 }
612 $xml_msg=~ s/<xxx><\/xxx>//;
614 # Return message
615 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
616 return ( $xml_msg );
617 }
619 ################################
620 # @brief Set product properities for a specific host or globaly. Message needs one xml tag 'item' and within one xml tag 'name' and 'value'. The xml tags action and state are optional.
621 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
622 # @param msg_hash - HASHREF - message information parsed into a hash
623 # @param session_id - INTEGER - POE session id of the processing of this message
624 # @return out_msg - STRING - feedback to GOsa in success and error case
625 sub opsi_set_product_properties {
626 my $startTime = Time::HiRes::time;
627 my ($msg, $msg_hash, $session_id) = @_;
628 my $header = @{$msg_hash->{'header'}}[0];
629 my $source = @{$msg_hash->{'source'}}[0];
630 my $target = @{$msg_hash->{'target'}}[0];
631 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
632 my ($productId, $hostId);
634 # Build return message with twisted target and source
635 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
636 if (defined $forward_to_gosa) {
637 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
638 }
640 # Sanity check of needed parameter
641 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
642 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
643 &add_content2xml_hash($out_hash, "error", "productId");
644 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
645 return ( &create_xml_string($out_hash) );
646 }
647 if (not exists $msg_hash->{'item'}) {
648 &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
649 &add_content2xml_hash($out_hash, "error", "item");
650 &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1);
651 return ( &create_xml_string($out_hash) );
652 } else {
653 if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
654 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
655 &add_content2xml_hash($out_hash, "error", "name");
656 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1);
657 return ( &create_xml_string($out_hash) );
658 }
659 if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
660 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
661 &add_content2xml_hash($out_hash, "error", "value");
662 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1);
663 return ( &create_xml_string($out_hash) );
664 }
665 }
666 # if no hostId is given, set_product_properties will act on globally
667 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1)) {
668 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
669 &add_content2xml_hash($out_hash, "error", "hostId");
670 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
671 return ( &create_xml_string($out_hash) );
672 }
675 # Get productId
676 $productId = @{$msg_hash->{'productId'}}[0];
677 &add_content2xml_hash($out_hash, "productId", $productId);
679 # Get hostId if defined
680 if (exists $msg_hash->{'hostId'}){
681 $hostId = @{$msg_hash->{'hostId'}}[0];
682 &add_content2xml_hash($out_hash, "hostId", $hostId);
683 }
685 # Set product states if requested
686 if (defined @{$msg_hash->{'action'}}[0]){
687 &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
688 }
689 if (defined @{$msg_hash->{'state'}}[0]){
690 &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
691 }
693 # Find properties
694 foreach my $item (@{$msg_hash->{'item'}}){
695 # JSON Query
696 my $callobj;
698 if (defined $hostId){
699 $callobj = {
700 method => 'setProductProperty',
701 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
702 id => 1,
703 };
704 } else {
705 $callobj = {
706 method => 'setProductProperty',
707 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
708 id => 1,
709 };
710 }
712 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
713 my ($res_err, $res_err_string) = &check_opsi_res($res);
715 if ($res_err){
716 &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
717 &add_content2xml_hash($out_hash, "error", $res_err_string);
718 }
719 }
722 # Return message
723 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
724 return ( &create_xml_string($out_hash) );
725 }
727 ################################
728 # @brief Reports client hardware inventory.
729 # @param msg - STRING - xml message with tag hostId
730 # @param msg_hash - HASHREF - message information parsed into a hash
731 # @param session_id - INTEGER - POE session id of the processing of this message
732 # @return out_msg - STRING - feedback to GOsa in success and error case
733 sub opsi_get_client_hardware {
734 my $startTime = Time::HiRes::time;
735 my ($msg, $msg_hash, $session_id) = @_;
736 my $header = @{$msg_hash->{'header'}}[0];
737 my $source = @{$msg_hash->{'source'}}[0];
738 my $target = @{$msg_hash->{'target'}}[0];
739 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
740 my $hostId;
741 my $error = 0;
742 my $xml_msg;
744 # Sanity check of needed parameter
745 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
746 $hostId = @{$msg_hash->{'hostId'}}[0];
747 } else {
748 return &_giveErrorFeedback($msg_hash, $_, $session_id);
749 }
752 # Build return message with twisted target and source
753 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
754 if (defined $forward_to_gosa) {
755 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
756 }
757 &add_content2xml_hash($out_hash, "hostId", "$hostId");
758 &add_content2xml_hash($out_hash, "xxx", "");
760 # Move to XML string
761 $xml_msg= &create_xml_string($out_hash);
763 my $res = &_callOpsi(method=>'getHardwareInformation_hash', params=>[ $hostId ]);
764 if (not &check_opsi_res($res)){
765 my $result= $res->result;
766 if (ref $result eq "HASH") {
767 foreach my $r (keys %{$result}){
768 my $item= "\n<item><id>".xml_quote($r)."</id>";
769 my $value= $result->{$r};
770 foreach my $sres (@{$value}){
772 foreach my $dres (keys %{$sres}){
773 if (defined $sres->{$dres}){
774 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
775 }
776 }
778 }
779 $item.= "</item>";
780 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
782 }
783 }
784 }
786 $xml_msg=~ s/<xxx><\/xxx>//;
788 # Return message
789 my $endTime = Time::HiRes::time;
790 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
791 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
792 return ( $xml_msg );
793 }
795 ################################
796 # @brief Reports all Opsi clients.
797 # @param msg - STRING - xml message
798 # @param msg_hash - HASHREF - message information parsed into a hash
799 # @param session_id - INTEGER - POE session id of the processing of this message
800 # @return out_msg - STRING - feedback to GOsa in success and error case
801 sub opsi_list_clients {
802 my $startTime = Time::HiRes::time;
803 my ($msg, $msg_hash, $session_id) = @_;
804 my $header = @{$msg_hash->{'header'}}[0];
805 my $source = @{$msg_hash->{'source'}}[0];
806 my $target = @{$msg_hash->{'target'}}[0];
807 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
809 # Build return message with twisted target and source
810 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
811 if (defined $forward_to_gosa) {
812 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
813 }
814 &add_content2xml_hash($out_hash, "xxx", "");
816 # Move to XML string
817 my $xml_msg= &create_xml_string($out_hash);
819 # JSON Query
820 my $callobj = {
821 method => 'getClientsInformation_listOfHashes',
822 params => [ ],
823 id => 1,
824 };
826 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
827 if (not &check_opsi_res($res)){
828 foreach my $host (@{$res->result}){
829 my $item= "\n<item><name>".$host->{'hostId'}."</name>";
830 $item.= "<mac>".xml_quote($host->{'macAddress'})."</mac>";
831 if (defined($host->{'description'})){
832 $item.= "<description>".xml_quote($host->{'description'})."</description>";
833 }
834 if (defined($host->{'notes'})){
835 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
836 }
837 if (defined($host->{'lastSeen'})){
838 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
839 }
841 $item.= "</item>";
842 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
843 }
844 }
845 $xml_msg=~ s/<xxx><\/xxx>//;
847 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
848 return ( $xml_msg );
849 }
851 ################################
852 # @brief Reports client software inventory.
853 # @param msg - STRING - xml message with tag hostId
854 # @param msg_hash - HASHREF - message information parsed into a hash
855 # @param session_id - INTEGER - POE session id of the processing of this message
856 # @return out_msg - STRING - feedback to GOsa in success and error case
857 sub opsi_get_client_software {
858 my $startTime = Time::HiRes::time;
859 my ($msg, $msg_hash, $session_id) = @_;
860 my $header = @{$msg_hash->{'header'}}[0];
861 my $source = @{$msg_hash->{'source'}}[0];
862 my $target = @{$msg_hash->{'target'}}[0];
863 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
864 my $error = 0;
865 my $hostId;
866 my $xml_msg;
868 # Build return message with twisted target and source
869 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
870 if (defined $forward_to_gosa) {
871 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
872 }
874 # Sanity check of needed parameter
875 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
876 $error++;
877 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
878 &add_content2xml_hash($out_hash, "error", "hostId");
879 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
880 }
882 if (not $error) {
884 # Get hostId
885 $hostId = @{$msg_hash->{'hostId'}}[0];
886 &add_content2xml_hash($out_hash, "hostId", "$hostId");
887 &add_content2xml_hash($out_hash, "xxx", "");
888 }
890 $xml_msg= &create_xml_string($out_hash);
892 if (not $error) {
894 # JSON Query
895 my $callobj = {
896 method => 'getSoftwareInformation_hash',
897 params => [ $hostId ],
898 id => 1,
899 };
901 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
902 if (not &check_opsi_res($res)){
903 my $result= $res->result;
904 }
906 $xml_msg=~ s/<xxx><\/xxx>//;
908 }
910 # Return message
911 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
912 return ( $xml_msg );
913 }
915 ################################
916 # @brief Reports product for given hostId or globally.
917 # @param msg - STRING - xml message with optional tag hostId
918 # @param msg_hash - HASHREF - message information parsed into a hash
919 # @param session_id - INTEGER - POE session id of the processing of this message
920 # @return out_msg - STRING - feedback to GOsa in success and error case
921 sub opsi_get_local_products {
922 my $startTime = Time::HiRes::time;
923 my ($msg, $msg_hash, $session_id) = @_;
924 my $header = @{$msg_hash->{'header'}}[0];
925 my $source = @{$msg_hash->{'source'}}[0];
926 my $target = @{$msg_hash->{'target'}}[0];
927 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
928 my $hostId;
930 # Build return message with twisted target and source
931 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
932 if (defined $forward_to_gosa) {
933 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
934 }
935 &add_content2xml_hash($out_hash, "xxx", "");
937 # Get hostId if defined
938 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
939 $hostId = @{$msg_hash->{'hostId'}}[0];
940 &add_content2xml_hash($out_hash, "hostId", $hostId);
941 }
943 my $callobj;
945 # Move to XML string
946 my $xml_msg= &create_xml_string($out_hash);
948 # Check if we need to get host or global information
949 if (defined $hostId){
950 $callobj = {
951 method => 'getProductHostInformation_list',
952 params => [ $hostId ],
953 id => 1,
954 };
956 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
957 if (not &check_opsi_res($res)){
958 foreach my $product (@{$res->result}){
959 my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><state>".xml_quote($product->{'installationStatus'})."</state><action>".xml_quote($product->{'actionRequest'})."</action><\/item><xxx><\/xxx>";
960 $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
961 }
962 }
964 } else {
966 # For hosts, only return the products that are or get installed
967 $callobj = {
968 method => 'getProductInformation_list',
969 params => [ undef, 'localboot' ],
970 id => 1,
971 };
973 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
974 if (not &check_opsi_res($res)){
975 foreach my $product (@{$res->result}) {
976 my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
977 $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
978 }
979 }
980 }
982 $xml_msg=~ s/<xxx><\/xxx>//;
984 # Retrun Message
985 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
986 return ( $xml_msg );
987 }
989 ################################
990 # @brief Deletes a client from Opsi.
991 # @param msg - STRING - xml message with tag hostId
992 # @param msg_hash - HASHREF - message information parsed into a hash
993 # @param session_id - INTEGER - POE session id of the processing of this message
994 # @return out_msg - STRING - feedback to GOsa in success and error case
995 sub opsi_del_client {
996 my $startTime = Time::HiRes::time;
997 my ($msg, $msg_hash, $session_id) = @_;
998 my $header = @{$msg_hash->{'header'}}[0];
999 my $source = @{$msg_hash->{'source'}}[0];
1000 my $target = @{$msg_hash->{'target'}}[0];
1001 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1002 my $hostId;
1003 my $error = 0;
1005 # Build return message with twisted target and source
1006 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1007 if (defined $forward_to_gosa) {
1008 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1009 }
1011 # Sanity check of needed parameter
1012 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1013 $error++;
1014 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1015 &add_content2xml_hash($out_hash, "error", "hostId");
1016 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
1017 }
1019 if (not $error) {
1021 # Get hostId
1022 $hostId = @{$msg_hash->{'hostId'}}[0];
1023 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1025 # JSON Query
1026 my $callobj = {
1027 method => 'deleteClient',
1028 params => [ $hostId ],
1029 id => 1,
1030 };
1031 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1032 }
1034 # Move to XML string
1035 my $xml_msg= &create_xml_string($out_hash);
1037 # Return message
1038 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1039 return ( $xml_msg );
1040 }
1042 ################################
1043 # @brief Set a client in Opsi to install and trigger a wake on lan message (WOL).
1044 # @param msg - STRING - xml message with tags hostId, macaddress
1045 # @param msg_hash - HASHREF - message information parsed into a hash
1046 # @param session_id - INTEGER - POE session id of the processing of this message
1047 # @return out_msg - STRING - feedback to GOsa in success and error case
1048 sub opsi_install_client {
1049 my $startTime = Time::HiRes::time;
1050 my ($msg, $msg_hash, $session_id) = @_;
1051 my $header = @{$msg_hash->{'header'}}[0];
1052 my $source = @{$msg_hash->{'source'}}[0];
1053 my $target = @{$msg_hash->{'target'}}[0];
1054 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1055 my ($hostId, $macaddress);
1056 my $error = 0;
1057 my @out_msg_l;
1059 # Build return message with twisted target and source
1060 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1061 if (defined $forward_to_gosa) {
1062 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1063 }
1065 # Sanity check of needed parameter
1066 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1067 $error++;
1068 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1069 &add_content2xml_hash($out_hash, "error", "hostId");
1070 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
1071 }
1072 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') ) {
1073 $error++;
1074 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1075 &add_content2xml_hash($out_hash, "error", "macaddress");
1076 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
1077 } else {
1078 if ((exists $msg_hash->{'macaddress'}) &&
1079 ($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)) {
1080 $macaddress = $1;
1081 } else {
1082 $error ++;
1083 &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1084 &add_content2xml_hash($out_hash, "error", "macaddress");
1085 &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1);
1086 }
1087 }
1089 if (not $error) {
1091 # Get hostId
1092 $hostId = @{$msg_hash->{'hostId'}}[0];
1093 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1095 # Load all products for this host with status != "not_installed" or actionRequest != "none"
1096 my $callobj = {
1097 method => 'getProductStates_hash',
1098 params => [ $hostId ],
1099 id => 1,
1100 };
1102 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1103 if (not &check_opsi_res($hres)){
1104 my $htmp= $hres->result->{$hostId};
1106 # check state != not_installed or action == setup -> load and add
1107 foreach my $product (@{$htmp}){
1108 # Now we've a couple of hashes...
1109 if ($product->{'installationStatus'} ne "not_installed" or
1110 $product->{'actionRequest'} ne "none"){
1112 # Do an action request for all these -> "setup".
1113 $callobj = {
1114 method => 'setProductActionRequest',
1115 params => [ $product->{'productId'}, $hostId, "setup" ],
1116 id => 1,
1117 };
1118 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1119 my ($res_err, $res_err_string) = &check_opsi_res($res);
1120 if ($res_err){
1121 &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1122 } else {
1123 &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1124 }
1125 }
1126 }
1127 }
1128 push(@out_msg_l, &create_xml_string($out_hash));
1131 # Build wakeup message for client
1132 if (not $error) {
1133 my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1134 &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1135 my $wakeup_msg = &create_xml_string($wakeup_hash);
1136 push(@out_msg_l, $wakeup_msg);
1138 # invoke trigger wake for this gosa-si-server
1139 &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1140 }
1141 }
1143 # Return messages
1144 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1145 return @out_msg_l;
1146 }
1148 ################################
1149 # @brief Set action for an Opsi client
1150 # @param product - STRING - Opsi product
1151 # @param action - STRING - action
1152 # @param hostId - STRING - Opsi hostId
1153 sub _set_action {
1154 my $product= shift;
1155 my $action = shift;
1156 my $hostId = shift;
1157 my $callobj;
1159 $callobj = {
1160 method => 'setProductActionRequest',
1161 params => [ $product, $hostId, $action],
1162 id => 1,
1163 };
1165 $main::opsi_client->call($main::opsi_url, $callobj);
1166 }
1168 ################################
1169 # @brief Set state for an Opsi client
1170 # @param product - STRING - Opsi product
1171 # @param action - STRING - state
1172 # @param hostId - STRING - Opsi hostId
1173 sub _set_state {
1174 my $product = shift;
1175 my $state = shift;
1176 my $hostId = shift;
1177 my $callobj;
1179 $callobj = {
1180 method => 'setProductState',
1181 params => [ $product, $hostId, $state ],
1182 id => 1,
1183 };
1185 $main::opsi_client->call($main::opsi_url, $callobj);
1186 }
1188 ################################
1189 # @brief Create a license pool at Opsi server.
1190 # @param licensePoolId The name of the pool (optional).
1191 # @param description The description of the pool (optional).
1192 # @param productIds A list of assigned porducts of the pool (optional).
1193 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional).
1194 sub opsi_createLicensePool {
1195 my $startTime = Time::HiRes::time;
1196 my ($msg, $msg_hash, $session_id) = @_;
1197 my $header = @{$msg_hash->{'header'}}[0];
1198 my $source = @{$msg_hash->{'source'}}[0];
1199 my $target = @{$msg_hash->{'target'}}[0];
1200 my $out_hash;
1201 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1202 my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1203 my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1204 my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1206 # Create license Pool
1207 my $callobj = {
1208 method => 'createLicensePool',
1209 params => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1210 id => 1,
1211 };
1212 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1214 # Check Opsi error
1215 my ($res_error, $res_error_str) = &check_opsi_res($res);
1216 if ($res_error){
1217 # Create error message
1218 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1219 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1220 return ( &create_xml_string($out_hash) );
1221 }
1223 # Create function result message
1224 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1225 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1227 my $endTime = Time::HiRes::time;
1228 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1229 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1230 return ( &create_xml_string($out_hash) );
1231 }
1233 ################################
1234 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1235 sub opsi_getLicensePools_listOfHashes {
1236 my $startTime = Time::HiRes::time;
1237 my ($msg, $msg_hash, $session_id) = @_;
1238 my $header = @{$msg_hash->{'header'}}[0];
1239 my $source = @{$msg_hash->{'source'}}[0];
1240 my $out_hash;
1242 # Fetch infos from Opsi server
1243 my $callobj = {
1244 method => 'getLicensePools_listOfHashes',
1245 params => [ ],
1246 id => 1,
1247 };
1248 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1250 # Check Opsi error
1251 my ($res_error, $res_error_str) = &check_opsi_res($res);
1252 if ($res_error){
1253 # Create error message
1254 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1255 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1256 return ( &create_xml_string($out_hash) );
1257 }
1259 # Create function result message
1260 my $res_hash = { 'hit'=> [] };
1261 foreach my $licensePool ( @{$res->result}) {
1262 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1263 'description' => [$licensePool->{'description'}],
1264 'productIds' => $licensePool->{'productIds'},
1265 'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1266 };
1267 push( @{$res_hash->{hit}}, $licensePool_hash );
1268 }
1270 # Create function result message
1271 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1272 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1273 $out_hash->{result} = [$res_hash];
1275 my $endTime = Time::HiRes::time;
1276 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1277 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1278 return ( &create_xml_string($out_hash) );
1279 }
1281 ################################
1282 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1283 # @param licensePoolId The name of the pool.
1284 sub opsi_getLicensePool_hash {
1285 my $startTime = Time::HiRes::time;
1286 my ($msg, $msg_hash, $session_id) = @_;
1287 my $header = @{$msg_hash->{'header'}}[0];
1288 my $source = @{$msg_hash->{'source'}}[0];
1289 my $target = @{$msg_hash->{'target'}}[0];
1290 my $licensePoolId;
1291 my $out_hash;
1293 # Check input sanity
1294 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1295 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1296 } else {
1297 return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
1298 }
1300 # Fetch infos from Opsi server
1301 my $callobj = {
1302 method => 'getLicensePool_hash',
1303 params => [ $licensePoolId ],
1304 id => 1,
1305 };
1306 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1308 # Check Opsi error
1309 my ($res_error, $res_error_str) = &check_opsi_res($res);
1310 if ($res_error){
1311 # Create error message
1312 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1313 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1314 &add_content2xml_hash($out_hash, "error", $res_error_str);
1315 return ( &create_xml_string($out_hash) );
1316 }
1318 # Create function result message
1319 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1320 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1321 &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1322 &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1323 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1324 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1326 my $endTime = Time::HiRes::time;
1327 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1328 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1329 return ( &create_xml_string($out_hash) );
1330 }
1332 sub _parse_getSoftwareLicenseUsages {
1333 my $res = shift;
1335 # Parse Opsi result
1336 my $tmp_licensePool_cache = {};
1337 my $res_hash = { 'hit'=> [] };
1338 foreach my $license ( @{$res}) {
1339 my $tmp_licensePool = $license->{'licensePoolId'};
1340 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1341 # Fetch missing informations from Opsi and cache the results for a possible later usage
1342 my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1343 if (not $err) {
1344 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1345 }
1346 }
1347 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1348 'notes' => [$license->{'notes'}],
1349 'licenseKey' => [$license->{'licenseKey'}],
1350 'hostId' => [$license->{'hostId'}],
1351 'licensePoolId' => [$tmp_licensePool],
1352 };
1353 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1354 $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1355 map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1356 map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1357 }
1358 push( @{$res_hash->{hit}}, $license_hash );
1359 }
1361 return $res_hash;
1362 }
1364 ################################
1365 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1366 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1367 # @param licensePoolId The name of the pool (optional).
1368 sub opsi_getSoftwareLicenseUsages {
1369 my $startTime = Time::HiRes::time;
1370 my ($msg, $msg_hash, $session_id) = @_;
1371 my $header = @{$msg_hash->{'header'}}[0];
1372 my $source = @{$msg_hash->{'source'}}[0];
1373 my $target = @{$msg_hash->{'target'}}[0];
1374 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1375 my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1376 my $out_hash;
1378 my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1379 if ($err){
1380 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1381 }
1383 # Parse Opsi result
1384 my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1386 # Create function result message
1387 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1388 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1389 $out_hash->{result} = [$res_hash];
1391 my $endTime = Time::HiRes::time;
1392 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1393 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1394 return ( &create_xml_string($out_hash) );
1395 }
1397 ################################
1398 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1399 # @param productId Something like 'firefox', 'python' or anything else .
1400 sub opsi_getSoftwareLicenseUsagesForProductId {
1401 my $startTime = Time::HiRes::time;
1402 my ($msg, $msg_hash, $session_id) = @_;
1403 my $header = @{$msg_hash->{'header'}}[0];
1404 my $source = @{$msg_hash->{'source'}}[0];
1406 # Check input sanity
1407 my $productId;
1408 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1409 $productId= @{$msg_hash->{'productId'}}[0];
1410 } else {
1411 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1412 }
1414 # Fetch licensePoolId for productId
1415 my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1416 if ($err){
1417 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1418 }
1420 my $licensePoolId;
1422 # Fetch softwareLiceceUsages for licensePoolId
1423 ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1424 if ($err){
1425 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1426 }
1428 # Parse Opsi result
1429 my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1431 # Create function result message
1432 my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
1433 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1434 $out_hash->{result} = [$res_hash];
1436 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1437 return ( &create_xml_string($out_hash) );
1438 }
1440 ################################
1441 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1442 # @param softwareLicenseId Identificator of a license.
1443 sub opsi_getSoftwareLicense_hash {
1444 my $startTime = Time::HiRes::time;
1445 my ($msg, $msg_hash, $session_id) = @_;
1446 my $header = @{$msg_hash->{'header'}}[0];
1447 my $source = @{$msg_hash->{'source'}}[0];
1448 my $target = @{$msg_hash->{'target'}}[0];
1449 my $softwareLicenseId;
1450 my $out_hash;
1452 # Check input sanity
1453 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1454 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1455 } else {
1456 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1457 }
1459 my $callobj = {
1460 method => 'getSoftwareLicense_hash',
1461 params => [ $softwareLicenseId ],
1462 id => 1,
1463 };
1464 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1466 # Check Opsi error
1467 my ($res_error, $res_error_str) = &check_opsi_res($res);
1468 if ($res_error){
1469 # Create error message
1470 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1471 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1472 return ( &create_xml_string($out_hash) );
1473 }
1475 # Create function result message
1476 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1477 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1478 &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1479 &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1480 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1481 &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1482 foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1483 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1484 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1485 }
1487 my $endTime = Time::HiRes::time;
1488 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1489 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1490 return ( &create_xml_string($out_hash) );
1491 }
1493 ################################
1494 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool.
1495 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted.
1496 # @param licensePoolId The name of the pool.
1497 sub opsi_deleteLicensePool {
1498 my $startTime = Time::HiRes::time;
1499 my ($msg, $msg_hash, $session_id) = @_;
1500 my $header = @{$msg_hash->{'header'}}[0];
1501 my $source = @{$msg_hash->{'source'}}[0];
1502 my $target = @{$msg_hash->{'target'}}[0];
1503 my $licensePoolId;
1504 my $out_hash;
1506 # Check input sanity
1507 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1508 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1509 } else {
1510 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1511 }
1513 # Fetch softwareLicenseIds used in license pool
1514 # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1515 # but not the license contracts of the software licenses. In our case each software license has exactly one license contract.
1516 my $callobj = {
1517 method => 'getSoftwareLicenses_listOfHashes',
1518 params => [ ],
1519 id => 1,
1520 };
1521 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1523 # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1524 my @lCI_toBeDeleted;
1525 foreach my $softwareLicenseHash ( @{$res->result} ) {
1526 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) {
1527 next;
1528 }
1529 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1530 }
1532 # Delete license pool at Opsi server
1533 $callobj = {
1534 method => 'deleteLicensePool',
1535 params => [ $licensePoolId, 'deleteLicenses=True' ],
1536 id => 1,
1537 };
1538 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1539 my ($res_error, $res_error_str) = &check_opsi_res($res);
1540 if ($res_error){
1541 # Create error message
1542 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1543 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1544 return ( &create_xml_string($out_hash) );
1545 }
1547 # Delete each license contract connected with the license pool
1548 foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1549 my $callobj = {
1550 method => 'deleteLicenseContract',
1551 params => [ $licenseContractId ],
1552 id => 1,
1553 };
1554 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1555 my ($res_error, $res_error_str) = &check_opsi_res($res);
1556 if ($res_error){
1557 # Create error message
1558 &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1559 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1560 return ( &create_xml_string($out_hash) );
1561 }
1562 }
1564 # Create function result message
1565 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1566 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1568 my $endTime = Time::HiRes::time;
1569 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1570 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1571 return ( &create_xml_string($out_hash) );
1572 }
1574 ################################
1575 # @brief Create a license contract, create a software license and add the software license to the license pool
1576 # @param licensePoolId The name of the pool the license should be assigned.
1577 # @param licenseKey The license key.
1578 # @param partner Name of the license partner (optional).
1579 # @param conclusionDate Date of conclusion of license contract (optional)
1580 # @param notificationDate Date of notification that license is running out soon (optional).
1581 # @param notes This is the place for some notes (optional)
1582 # @param softwareLicenseId Identificator of a license (optional).
1583 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1584 # @param maxInstallations The number of clients use this license (optional).
1585 # @param boundToHost The name of the client the license is bound to (optional).
1586 # @param expirationDate The date when the license is running down (optional).
1587 sub opsi_createLicense {
1588 my $startTime = Time::HiRes::time;
1589 my ($msg, $msg_hash, $session_id) = @_;
1590 my $header = @{$msg_hash->{'header'}}[0];
1591 my $source = @{$msg_hash->{'source'}}[0];
1592 my $target = @{$msg_hash->{'target'}}[0];
1593 my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1594 my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1595 my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1596 my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1597 my $licenseContractId = undef;
1598 my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1599 my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1600 my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1601 my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1602 my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1603 my $licensePoolId;
1604 my $licenseKey;
1605 my $out_hash;
1607 # Check input sanity
1608 if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1609 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1610 } else {
1611 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1612 }
1613 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1614 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1615 } else {
1616 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1617 }
1618 if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1619 return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
1620 }
1622 # Automatically define licenseContractId if ID is not given
1623 if (defined $softwareLicenseId) {
1624 $licenseContractId = "c_".$softwareLicenseId;
1625 }
1627 # Create license contract at Opsi server
1628 my $callobj = {
1629 method => 'createLicenseContract',
1630 params => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1631 id => 1,
1632 };
1633 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1635 # Check Opsi error
1636 my ($res_error, $res_error_str) = &check_opsi_res($res);
1637 if ($res_error){
1638 # Create error message
1639 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1640 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1641 return ( &create_xml_string($out_hash) );
1642 }
1644 $licenseContractId = $res->result;
1646 # Create software license at Opsi server
1647 $callobj = {
1648 method => 'createSoftwareLicense',
1649 params => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1650 id => 1,
1651 };
1652 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1654 # Check Opsi error
1655 ($res_error, $res_error_str) = &check_opsi_res($res);
1656 if ($res_error){
1657 # Create error message
1658 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1659 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1660 return ( &create_xml_string($out_hash) );
1661 }
1663 $softwareLicenseId = $res->result;
1665 # Add software license to license pool
1666 $callobj = {
1667 method => 'addSoftwareLicenseToLicensePool',
1668 params => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1669 id => 1,
1670 };
1671 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1673 # Check Opsi error
1674 ($res_error, $res_error_str) = &check_opsi_res($res);
1675 if ($res_error){
1676 # Create error message
1677 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1678 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1679 return ( &create_xml_string($out_hash) );
1680 }
1682 # Create function result message
1683 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1684 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1686 my $endTime = Time::HiRes::time;
1687 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1688 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1689 return ( &create_xml_string($out_hash) );
1690 }
1692 ################################
1693 # @brief Assign a software license to a host
1694 # @param hostid Something like client_1.intranet.mydomain.de
1695 # @param licensePoolId The name of the pool.
1696 sub opsi_assignSoftwareLicenseToHost {
1697 my $startTime = Time::HiRes::time;
1698 my ($msg, $msg_hash, $session_id) = @_;
1699 my $header = @{$msg_hash->{'header'}}[0];
1700 my $source = @{$msg_hash->{'source'}}[0];
1701 my $target = @{$msg_hash->{'target'}}[0];
1702 my $hostId;
1703 my $licensePoolId;
1705 # Check input sanity
1706 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1707 $hostId = @{$msg_hash->{'hostId'}}[0];
1708 } else {
1709 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1710 }
1711 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1712 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1713 } else {
1714 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1715 }
1717 # Assign a software license to a host
1718 my $callobj = {
1719 method => 'getAndAssignSoftwareLicenseKey',
1720 params => [ $hostId, $licensePoolId ],
1721 id => 1,
1722 };
1723 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1725 # Check Opsi error
1726 my ($res_error, $res_error_str) = &check_opsi_res($res);
1727 if ($res_error){
1728 # Create error message
1729 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1730 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1731 return ( &create_xml_string($out_hash) );
1732 }
1734 # Create function result message
1735 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1736 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1738 my $endTime = Time::HiRes::time;
1739 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1740 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1741 return ( &create_xml_string($out_hash) );
1742 }
1744 ################################
1745 # @brief Unassign a software license from a host.
1746 # @param hostid Something like client_1.intranet.mydomain.de
1747 # @param licensePoolId The name of the pool.
1748 sub opsi_unassignSoftwareLicenseFromHost {
1749 my $startTime = Time::HiRes::time;
1750 my ($msg, $msg_hash, $session_id) = @_;
1751 my $header = @{$msg_hash->{'header'}}[0];
1752 my $source = @{$msg_hash->{'source'}}[0];
1753 my $target = @{$msg_hash->{'target'}}[0];
1754 my $hostId;
1755 my $licensePoolId;
1757 # Check input sanity
1758 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1759 $hostId = @{$msg_hash->{'hostId'}}[0];
1760 } else {
1761 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1762 }
1763 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1764 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1765 } else {
1766 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1767 }
1769 # Unassign a software license from a host
1770 my $callobj = {
1771 method => 'deleteSoftwareLicenseUsage',
1772 params => [ $hostId, '', $licensePoolId ],
1773 id => 1,
1774 };
1775 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1777 # Check Opsi error
1778 my ($res_error, $res_error_str) = &check_opsi_res($res);
1779 if ($res_error){
1780 # Create error message
1781 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1782 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1783 return ( &create_xml_string($out_hash) );
1784 }
1786 # Create function result message
1787 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1788 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1790 my $endTime = Time::HiRes::time;
1791 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1792 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1793 return ( &create_xml_string($out_hash) );
1794 }
1796 ################################
1797 # @brief Unassign all software licenses from a host
1798 # @param hostid Something like client_1.intranet.mydomain.de
1799 sub opsi_unassignAllSoftwareLicensesFromHost {
1800 my $startTime = Time::HiRes::time;
1801 my ($msg, $msg_hash, $session_id) = @_;
1802 my $header = @{$msg_hash->{'header'}}[0];
1803 my $source = @{$msg_hash->{'source'}}[0];
1804 my $target = @{$msg_hash->{'target'}}[0];
1805 my $hostId;
1807 # Check input sanity
1808 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1809 $hostId = @{$msg_hash->{'hostId'}}[0];
1810 } else {
1811 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1812 }
1814 # Unassign all software licenses from a host
1815 my $callobj = {
1816 method => 'deleteAllSoftwareLicenseUsages',
1817 params => [ $hostId ],
1818 id => 1,
1819 };
1820 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1822 # Check Opsi error
1823 my ($res_error, $res_error_str) = &check_opsi_res($res);
1824 if ($res_error){
1825 # Create error message
1826 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1827 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1828 return ( &create_xml_string($out_hash) );
1829 }
1831 # Create function result message
1832 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1833 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1835 my $endTime = Time::HiRes::time;
1836 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1837 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1838 return ( &create_xml_string($out_hash) );
1839 }
1842 ################################
1843 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1844 # and the number of max and remaining installations for a given OPSI product.
1845 # @param productId Identificator of an OPSI product.
1846 sub opsi_getLicenseInformationForProduct {
1847 my $startTime = Time::HiRes::time;
1848 my ($msg, $msg_hash, $session_id) = @_;
1849 my $header = @{$msg_hash->{'header'}}[0];
1850 my $source = @{$msg_hash->{'source'}}[0];
1851 my $productId;
1852 my $out_hash;
1854 # Check input sanity
1855 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1856 $productId = @{$msg_hash->{'productId'}}[0];
1857 } else {
1858 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1859 }
1861 # Fetch infos from Opsi server
1862 my $callobj = {
1863 method => 'getLicensePoolId',
1864 params => [ $productId ],
1865 id => 1,
1866 };
1867 #my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1868 my $res = $opsi_client->call($opsi_url, $callobj);
1870 # Check Opsi error
1871 my ($res_error, $res_error_str) = &check_opsi_res($res);
1872 if ($res_error){
1873 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
1874 }
1876 my $licensePoolId = $res->result;
1878 # Fetch statistic information for given pool ID
1879 $callobj = {
1880 method => 'getLicenseStatistics_hash',
1881 params => [ ],
1882 id => 1,
1883 };
1884 $res = $opsi_client->call($opsi_url, $callobj);
1886 # Check Opsi error
1887 ($res_error, $res_error_str) = &check_opsi_res($res);
1888 if ($res_error){
1889 # Create error message
1890 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
1891 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1892 return ( &create_xml_string($out_hash) );
1893 }
1895 # Create function result message
1896 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1897 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1898 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1899 &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
1900 &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
1901 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
1902 &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
1903 map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
1905 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1906 return ( &create_xml_string($out_hash) );
1907 }
1910 ################################
1911 # @brief Returns licensePoolId, description, a list of productIds, al list of windowsSoftwareIds and a list of licenses for a given licensePoolId.
1912 # Each license contains softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseKeys, hostIds, expirationDate, boundToHost and licenseContractId.
1913 # The licenseContract contains conclusionDate, expirationDate, notes, notificationDate and partner.
1914 # @param licensePoolId The name of the pool.
1915 sub opsi_getPool {
1916 my $startTime = Time::HiRes::time;
1917 my ($msg, $msg_hash, $session_id) = @_;
1918 my $header = @{$msg_hash->{'header'}}[0];
1919 my $source = @{$msg_hash->{'source'}}[0];
1921 # Check input sanity
1922 my $licensePoolId;
1923 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1924 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1925 } else {
1926 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1927 }
1929 # Create hash for the answer
1930 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1931 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1933 # Call Opsi
1934 my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
1935 if ($err){
1936 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
1937 }
1938 # Add data to outgoing hash
1939 &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
1940 &add_content2xml_hash($out_hash, "description", $res->{'description'});
1941 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
1942 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
1945 # Call Opsi two times
1946 my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1947 if ($usages_err){
1948 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
1949 }
1950 my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
1951 if ($licenses_err){
1952 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
1953 }
1955 # Add data to outgoing hash
1956 # Parse through all software licenses and select those associated to the pool
1957 my $res_hash = { 'hit'=> [] };
1958 foreach my $license ( @$licenses_res) {
1959 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
1960 my $found = 0;
1961 my @licensePoolIds_list = @{$license->{licensePoolIds}};
1962 foreach my $lPI ( @licensePoolIds_list) {
1963 if ($lPI eq $licensePoolId) { $found++ }
1964 }
1965 if (not $found ) { next; };
1966 # Found matching licensePoolId
1967 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1968 'licenseKeys' => {},
1969 'expirationDate' => [$license->{'expirationDate'}],
1970 'boundToHost' => [$license->{'boundToHost'}],
1971 'maxInstallations' => [$license->{'maxInstallations'}],
1972 'licenseType' => [$license->{'licenseType'}],
1973 'licenseContractId' => [$license->{'licenseContractId'}],
1974 'licensePoolIds' => [],
1975 'hostIds' => [],
1976 };
1977 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
1978 push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
1979 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
1980 }
1981 foreach my $usage (@$usages_res) {
1982 # Search for hostIds with matching softwareLicenseId
1983 if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
1984 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
1985 }
1986 }
1988 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
1989 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
1990 if ($lContract_err){
1991 return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
1992 }
1993 $license_hash->{$license->{'licenseContractId'}} = [];
1994 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
1995 'notificationDate' => [$lContract_res->{notificationDate}],
1996 'notes' => [$lContract_res->{notes}],
1997 'exirationDate' => [$lContract_res->{expirationDate}],
1998 'partner' => [$lContract_res->{partner}],
1999 };
2000 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2002 push( @{$res_hash->{hit}}, $license_hash );
2003 }
2004 $out_hash->{licenses} = [$res_hash];
2006 my $endTime = Time::HiRes::time;
2007 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2008 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2009 return ( &create_xml_string($out_hash) );
2010 }
2013 ################################
2014 # @brief Removes at first the software license from license pool and than deletes the software license.
2015 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2016 # @param softwareLicenseId Identificator of a license.
2017 # @param licensePoolId The name of the pool.
2018 sub opsi_removeLicense {
2019 my $startTime = Time::HiRes::time;
2020 my ($msg, $msg_hash, $session_id) = @_;
2021 my $header = @{$msg_hash->{'header'}}[0];
2022 my $source = @{$msg_hash->{'source'}}[0];
2024 # Check input sanity
2025 my $softwareLicenseId;
2026 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2027 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2028 } else {
2029 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2030 }
2031 my $licensePoolId;
2032 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2033 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2034 } else {
2035 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2036 }
2038 # Call Opsi
2039 my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2040 if ($err){
2041 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2042 }
2044 # Call Opsi
2045 ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2046 if ($err){
2047 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2048 }
2050 # Create hash for the answer
2051 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2052 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2053 my $endTime = Time::HiRes::time;
2054 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2055 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2056 return ( &create_xml_string($out_hash) );
2057 }
2060 ################################
2061 # @brief Return softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseContractId, expirationDate, boundToHost and a list of productIds.
2062 # @param hostId Something like client_1.intranet.mydomain.de
2063 sub opsi_getReservedLicenses {
2064 my $startTime = Time::HiRes::time;
2065 my ($msg, $msg_hash, $session_id) = @_;
2066 my $header = @{$msg_hash->{'header'}}[0];
2067 my $source = @{$msg_hash->{'source'}}[0];
2069 # Check input sanity
2070 my $hostId;
2071 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2072 $hostId = @{$msg_hash->{'hostId'}}[0];
2073 } else {
2074 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2075 }
2077 # Fetch informations from Opsi server
2078 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2079 if ($license_err){
2080 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2081 }
2083 # Parse result
2084 my $res_hash = { 'hit'=> [] };
2085 foreach my $license ( @$license_res) {
2086 if ($license->{boundToHost} ne $hostId) { next; }
2088 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2089 'maxInstallations' => [$license->{'maxInstallations'}],
2090 'boundToHost' => [$license->{'boundToHost'}],
2091 'expirationDate' => [$license->{'expirationDate'}],
2092 'licenseContractId' => [$license->{'licenseContractId'}],
2093 'licenseType' => [$license->{'licenseType'}],
2094 'licensePoolIds' => [],
2095 };
2097 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2098 # Fetch information for license pools containing a software license which is bound to given host
2099 my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2100 if ($pool_err){
2101 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2102 }
2104 # Add licensePool information to result hash
2105 push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2106 $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2107 map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2108 map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2109 }
2110 push( @{$res_hash->{hit}}, $license_hash );
2111 }
2112 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2113 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2114 $out_hash->{licenses} = [$res_hash];
2116 my $endTime = Time::HiRes::time;
2117 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2118 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2119 return ( &create_xml_string($out_hash) );
2120 }
2122 ################################
2123 # @brief Bound the given softwareLicenseId to the given host.
2124 # @param hostId Opsi hostId
2125 # @param softwareLicenseId Identificator of a license (optional).
2126 sub opsi_boundHostToLicense {
2127 my $startTime = Time::HiRes::time;
2128 my ($msg, $msg_hash, $session_id) = @_;
2129 my $header = @{$msg_hash->{'header'}}[0];
2130 my $source = @{$msg_hash->{'source'}}[0];
2132 # Check input sanity
2133 my $hostId;
2134 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2135 $hostId = @{$msg_hash->{'hostId'}}[0];
2136 } else {
2137 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2138 }
2139 my $softwareLicenseId;
2140 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2141 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2142 } else {
2143 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2144 }
2146 # Fetch informations from Opsi server
2147 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2148 if ($license_err){
2149 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2150 }
2152 # Memorize parameter for given softwareLicenseId
2153 my $licenseContractId;
2154 my $licenseType;
2155 my $maxInstallations;
2156 my $boundToHost;
2157 my $expirationDate = "";
2158 my $found;
2159 foreach my $license (@$license_res) {
2160 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2161 $licenseContractId = $license->{licenseContractId};
2162 $licenseType = $license->{licenseType};
2163 $maxInstallations = $license->{maxInstallations};
2164 $expirationDate = $license->{expirationDate};
2165 $found++;
2166 }
2168 if (not $found) {
2169 return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
2170 }
2172 # Set boundToHost option for a given software license
2173 my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId,
2174 'licenseContractId' => $licenseContractId,
2175 'licenseType' => $licenseType,
2176 'maxInstallations' => $maxInstallations,
2177 'boundToHost' => $hostId,
2178 'expirationDate' => $expirationDate);
2179 if ($bound_err) {
2180 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2181 }
2183 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2184 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2186 my $endTime = Time::HiRes::time;
2187 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2188 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2189 return ( &create_xml_string($out_hash) );
2190 }
2192 ################################
2193 # @brief Release a software license formerly bound to a host.
2194 # @param softwareLicenseId Identificator of a license.
2195 sub opsi_unboundHostFromLicense {
2196 # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2197 # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2198 # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2199 my $startTime = Time::HiRes::time;
2200 my ($msg, $msg_hash, $session_id) = @_;
2201 my $header = @{$msg_hash->{'header'}}[0];
2202 my $source = @{$msg_hash->{'source'}}[0];
2204 # Check input sanity
2205 my $softwareLicenseId;
2206 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2207 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2208 } else {
2209 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2210 }
2212 # Memorize parameter witch are required for this procedure
2213 my $licenseContractId;
2214 my $licenseType;
2215 my $maxInstallations;
2216 my $expirationDate;
2217 my $partner;
2218 my $conclusionDate;
2219 my $notificationDate;
2220 my $notes;
2221 my $licensePoolId;
2222 my $licenseKey;
2224 # Fetch license informations from Opsi server
2225 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2226 if ($license_err){
2227 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2228 }
2229 my $found = 0;
2230 foreach my $license (@$license_res) {
2231 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2232 $licenseContractId = $license->{licenseContractId};
2233 $licenseType = $license->{licenseType};
2234 $maxInstallations = $license->{maxInstallations};
2235 $expirationDate = $license->{expirationDate};
2236 $licensePoolId = @{$license->{licensePoolIds}}[0];
2237 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2238 $found++;
2239 }
2241 # Fetch contract informations from Opsi server
2242 my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2243 if ($contract_err){
2244 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2245 }
2246 $partner = $contract_res->{partner};
2247 $conclusionDate = $contract_res->{conclusionDate};
2248 $notificationDate = $contract_res->{notificationDate};
2249 $expirationDate = $contract_res->{expirationDate};
2250 $notes = $contract_res->{notes};
2252 # Delete software license
2253 my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2254 if ($err) {
2255 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2256 }
2258 # Recreate software license without boundToHost
2259 ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate,
2260 'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2261 if ($err) {
2262 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2263 }
2264 ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType,
2265 'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate );
2266 if ($err) {
2267 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2268 }
2269 ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2270 if ($err) {
2271 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2272 }
2274 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2275 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2277 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2278 return ( &create_xml_string($out_hash) );
2279 }
2281 ################################
2282 # @brief Returns a list of licenses with softwaerLicenseId, maxInstallations, boundToHost, expirationDate, licenseContractId, licenseType, a list of licensePoolIds with associated licenseKeys
2283 sub opsi_getAllSoftwareLicenses {
2284 my $startTime = Time::HiRes::time;
2285 my ($msg, $msg_hash, $session_id) = @_;
2286 my $header = @{$msg_hash->{'header'}}[0];
2287 my $source = @{$msg_hash->{'source'}}[0];
2289 my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
2290 if ($err) {
2291 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
2292 }
2294 # Parse result
2295 my $res_hash = { 'hit'=> [] };
2296 foreach my $license ( @$res) {
2297 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2298 'maxInstallations' => [$license->{'maxInstallations'}],
2299 'boundToHost' => [$license->{'boundToHost'}],
2300 'expirationDate' => [$license->{'expirationDate'}],
2301 'licenseContractId' => [$license->{'licenseContractId'}],
2302 'licenseType' => [$license->{'licenseType'}],
2303 'licensePoolIds' => [],
2304 'licenseKeys'=> {}
2305 };
2306 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2307 push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2308 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
2309 }
2310 push( @{$res_hash->{hit}}, $license_hash );
2311 }
2313 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2314 $out_hash->{licenses} = [$res_hash];
2315 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2317 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2318 return ( &create_xml_string($out_hash) );
2319 }
2321 sub opsi_test {
2322 my ($msg, $msg_hash, $session_id) = @_;
2323 my $header = @{$msg_hash->{'header'}}[0];
2324 my $source = @{$msg_hash->{'source'}}[0];
2325 my $pram1 = @{$msg_hash->{'productId'}}[0];
2327 print STDERR Dumper $pram1;
2329 # Fetch infos from Opsi server
2330 my $callobj = {
2331 method => 'getLicensePoolId',
2332 params => [ $pram1 ],
2333 id => 1,
2334 };
2335 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2337 print STDERR Dumper $res;
2338 return ();
2339 }
2342 # ----------------------------------------------------------------------------
2343 # internal methods handling the comunication with Opsi
2344 # ----------------------------------------------------------------------------
2346 ################################
2347 # @brief Checks if there is a specified tag and if the the tag has a content.
2348 sub _check_xml_tag_is_ok {
2349 my ($msg_hash,$tag) = @_;
2350 if (not defined $msg_hash->{$tag}) {
2351 $_ = "message contains no tag '$tag'";
2352 return 0;
2353 }
2354 if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
2355 $_ = "message tag '$tag' has no content";
2356 return 0;
2357 }
2358 return 1;
2359 }
2361 ################################
2362 # @brief Writes the log line and returns the error message for GOsa.
2363 sub _giveErrorFeedback {
2364 my ($msg_hash, $err_string, $session_id) = @_;
2365 &main::daemon_log("$session_id ERROR: $err_string", 1);
2366 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2367 if (exists $msg_hash->{forward_to_gosa}) {
2368 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
2369 }
2370 return ( &create_xml_string($out_hash) );
2371 }
2374 ################################
2375 # @brief Perform the call to the Opsi server and measure the time for the call
2376 sub _callOpsi {
2377 my %arg = ('method'=>undef, 'params'=>[], 'id'=>1, @_);
2379 my $callObject = {
2380 method => $arg{method},
2381 params => $arg{params},
2382 id => $arg{id},
2383 };
2385 my $startTime = Time::HiRes::time;
2386 my $opsiResult = $opsi_client->call($opsi_url, $callObject);
2387 my $endTime = Time::HiRes::time;
2388 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2390 &main::daemon_log("0 DEBUG: time to process opsi call '$arg{method}' : $elapsedTime seconds", 1034);
2392 return $opsiResult;
2393 }
2395 sub _getLicensePool_hash {
2396 my %arg = ( 'licensePoolId' => undef, @_ );
2398 if (not defined $arg{licensePoolId} ) {
2399 return ("function requires licensePoolId as parameter", 1);
2400 }
2402 my $res = &_callOpsi( method => 'getLicensePool_hash', params =>[$arg{licensePoolId}], id => 1 );
2403 my ($res_error, $res_error_str) = &check_opsi_res($res);
2404 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2406 return ($res->result, 0);
2407 }
2409 sub _getSoftwareLicenses_listOfHashes {
2411 my $res = &_callOpsi( method => 'getSoftwareLicenses_listOfHashes' );
2412 my ($res_error, $res_error_str) = &check_opsi_res($res);
2413 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2415 return ($res->result, 0);
2416 }
2418 sub _getSoftwareLicenseUsages_listOfHashes {
2419 my %arg = ( 'hostId' => "", 'licensePoolId' => "", @_ );
2421 my $res = &_callOpsi( method=>'getSoftwareLicenseUsages_listOfHashes', params=>[ $arg{hostId}, $arg{licensePoolId} ] );
2422 my ($res_error, $res_error_str) = &check_opsi_res($res);
2423 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2425 return ($res->result, 0);
2426 }
2428 sub _removeSoftwareLicenseFromLicensePool {
2429 my %arg = ( 'softwareLicenseId' => undef, 'licensePoolId' => undef, @_ );
2431 if (not defined $arg{softwareLicenseId} ) {
2432 return ("function requires softwareLicenseId as parameter", 1);
2433 }
2434 if (not defined $arg{licensePoolId} ) {
2435 return ("function requires licensePoolId as parameter", 1);
2436 }
2438 my $res = &_callOpsi( method=>'removeSoftwareLicenseFromLicensePool', params=>[ $arg{softwareLicenseId}, $arg{licensePoolId} ] );
2439 my ($res_error, $res_error_str) = &check_opsi_res($res);
2440 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2442 return ($res->result, 0);
2443 }
2445 sub _deleteSoftwareLicense {
2446 my %arg = ( 'softwareLicenseId' => undef, 'removeFromPools' => "false", @_ );
2448 if (not defined $arg{softwareLicenseId} ) {
2449 return ("function requires softwareLicenseId as parameter", 1);
2450 }
2451 my $removeFromPools = "";
2452 if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) {
2453 $removeFromPools = "removeFromPools";
2454 }
2456 my $res = &_callOpsi( method=>'deleteSoftwareLicense', params=>[ $arg{softwareLicenseId}, $removeFromPools ] );
2457 my ($res_error, $res_error_str) = &check_opsi_res($res);
2458 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2460 return ($res->result, 0);
2461 }
2463 sub _getLicensePoolId {
2464 my %arg = ( 'productId' => undef, @_ );
2466 if (not defined $arg{productId} ) {
2467 return ("function requires productId as parameter", 1);
2468 }
2470 my $res = &_callOpsi( method => 'getLicensePoolId', params => [ $arg{productId} ] );
2471 my ($res_error, $res_error_str) = &check_opsi_res($res);
2472 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2474 return ($res->result, 0);
2475 }
2477 sub _getLicenseContract_hash {
2478 my %arg = ( 'licenseContractId' => undef, @_ );
2480 if (not defined $arg{licenseContractId} ) {
2481 return ("function requires licenseContractId as parameter", 1);
2482 }
2484 my $res = &_callOpsi( method => 'getLicenseContract_hash', params => [ $arg{licenseContractId} ] );
2485 my ($res_error, $res_error_str) = &check_opsi_res($res);
2486 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2488 return ($res->result, 0);
2489 }
2491 sub _createLicenseContract {
2492 my %arg = (
2493 'licenseContractId' => undef,
2494 'partner' => undef,
2495 'conclusionDate' => undef,
2496 'notificationDate' => undef,
2497 'expirationDate' => undef,
2498 'notes' => undef,
2499 @_ );
2501 my $res = &_callOpsi( method => 'createLicenseContract',
2502 params => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2503 );
2504 my ($res_error, $res_error_str) = &check_opsi_res($res);
2505 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2507 return ($res->result, 0);
2508 }
2510 sub _createSoftwareLicense {
2511 my %arg = (
2512 'softwareLicenseId' => undef,
2513 'licenseContractId' => undef,
2514 'licenseType' => undef,
2515 'maxInstallations' => undef,
2516 'boundToHost' => undef,
2517 'expirationDate' => undef,
2518 @_ );
2520 my $res = &_callOpsi( method => 'createSoftwareLicense',
2521 params => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2522 );
2523 my ($res_error, $res_error_str) = &check_opsi_res($res);
2524 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2526 return ($res->result, 0);
2527 }
2529 sub _addSoftwareLicenseToLicensePool {
2530 my %arg = (
2531 'softwareLicenseId' => undef,
2532 'licensePoolId' => undef,
2533 'licenseKey' => undef,
2534 @_ );
2536 if (not defined $arg{softwareLicenseId} ) {
2537 return ("function requires softwareLicenseId as parameter", 1);
2538 }
2539 if (not defined $arg{licensePoolId} ) {
2540 return ("function requires licensePoolId as parameter", 1);
2541 }
2543 my $res = &_callOpsi( method => 'addSoftwareLicenseToLicensePool', params => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ] );
2544 my ($res_error, $res_error_str) = &check_opsi_res($res);
2545 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2547 return ($res->result, 0);
2548 }
2550 sub _getProductStates_hash {
2551 my %arg = ( 'hostId' => undef, @_ );
2553 if (not defined $arg{hostId} ) {
2554 return ("function requires hostId as parameter", 1);
2555 }
2557 my $res = &_callOpsi( method => 'getProductStates_hash', params => [$arg{hostId}]);
2558 my ($res_error, $res_error_str) = &check_opsi_res($res);
2559 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2561 return ($res->result, 0);
2562 }
2565 ################################
2566 # @brief Get all host information for a specific host.
2567 # @param msg - STRING - xml message with hostId tag
2568 # @param msg_hash - HASHREF - message information parsed into a hash
2569 # @param session_id - INTEGER - POE session id of the processing of this message
2570 # @return out_msg - STRING - feedback to GOsa in success and error case
2571 sub opsi_get_full_product_host_information {
2572 my $startTime = Time::HiRes::time;
2573 my ($msg, $msg_hash, $session_id) = @_;
2574 my $header = @{$msg_hash->{'header'}}[0];
2575 my $source = @{$msg_hash->{'source'}}[0];
2576 my $target = @{$msg_hash->{'target'}}[0];
2577 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
2578 my $hostId;
2579 my $xml_msg;
2581 # $callobj = {
2582 # method => 'getFullProductHostInformation_list',
2583 # params => [ $hostId ],
2584 # id => 1,
2585 # };
2586 # my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2587 # print STDERR "===================================================================\n";
2588 # print STDERR Dumper $res;
2589 # print STDERR "===================================================================\n";
2590 }
2592 1;