1 ## @file
2 # @details A GOsa-SI-server event module containing all functions for message handling.
3 # @brief Implementation of an event module for GOsa-SI-server.
6 package opsi_com;
7 use Exporter;
8 @ISA = qw(Exporter);
9 my @events = (
10 "get_events",
11 "opsi_install_client",
12 "opsi_get_netboot_products",
13 "opsi_get_local_products",
14 "opsi_get_client_hardware",
15 "opsi_get_client_software",
16 "opsi_get_product_properties",
17 "opsi_set_product_properties",
18 "opsi_list_clients",
19 "opsi_del_client",
20 "opsi_add_client",
21 "opsi_modify_client",
22 "opsi_add_product_to_client",
23 "opsi_del_product_from_client",
24 "opsi_createLicensePool",
25 "opsi_deleteLicensePool",
26 "opsi_createLicense",
27 "opsi_assignSoftwareLicenseToHost",
28 "opsi_unassignSoftwareLicenseFromHost",
29 "opsi_unassignAllSoftwareLicensesFromHost",
30 "opsi_getSoftwareLicense_hash",
31 "opsi_getLicensePool_hash",
32 "opsi_getSoftwareLicenseUsages",
33 "opsi_getSoftwareLicenseUsagesForProductId",
34 "opsi_getLicensePools_listOfHashes",
35 "opsi_getLicenseInformationForProduct",
36 "opsi_getPool",
37 "opsi_getAllSoftwareLicenses",
38 "opsi_removeLicense",
39 "opsi_getReservedLicenses",
40 "opsi_boundHostToLicense",
41 "opsi_unboundHostFromLicense",
42 "opsi_test",
43 );
44 @EXPORT = @events;
46 use strict;
47 use warnings;
48 use GOSA::GosaSupportDaemon;
49 use Data::Dumper;
50 use XML::Quote qw(:all);
52 BEGIN {}
54 END {}
56 # ----------------------------------------------------------------------------
57 # D E C L A R A T I O N S
58 # ----------------------------------------------------------------------------
60 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
61 my ($opsi_enabled, $opsi_server, $opsi_admin, $opsi_password, $opsi_url, $opsi_client);
62 my %cfg_defaults = (
63 "Opsi" => {
64 "enabled" => [\$opsi_enabled, "false"],
65 "server" => [\$opsi_server, "localhost"],
66 "admin" => [\$opsi_admin, "opsi-admin"],
67 "password" => [\$opsi_password, "secret"],
68 },
69 );
70 &read_configfile($main::cfg_file, %cfg_defaults);
71 if ($opsi_enabled eq "true") {
72 use JSON::RPC::Client;
73 use XML::Quote qw(:all);
74 use Time::HiRes qw( time );
75 $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
76 $opsi_client = new JSON::RPC::Client;
77 }
79 # ----------------------------------------------------------------------------
80 # external methods handling the comunication with GOsa/GOsa-si
81 # ----------------------------------------------------------------------------
83 ################################
84 # @brief A function returning a list of functions which are exported by importing the module.
85 # @return List of all provided functions
86 sub get_events {
87 return \@events;
88 }
90 ################################
91 # @brief Adds an Opsi product to an Opsi client.
92 # @param msg - STRING - xml message with tags hostId and productId
93 # @param msg_hash - HASHREF - message information parsed into a hash
94 # @param session_id - INTEGER - POE session id of the processing of this message
95 # @return out_msg - STRING - feedback to GOsa in success and error case
96 sub opsi_add_product_to_client {
97 my $startTime = Time::HiRes::time;
98 my ($msg, $msg_hash, $session_id) = @_;
99 my $header = @{$msg_hash->{'header'}}[0];
100 my $source = @{$msg_hash->{'source'}}[0];
101 my $target = @{$msg_hash->{'target'}}[0];
102 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
104 # Build return message
105 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
106 if (defined $forward_to_gosa) {
107 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
108 }
110 # Sanity check of needed parameter
111 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
112 return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
113 }
114 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
115 return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
116 }
118 # Get hostId
119 my $hostId = @{$msg_hash->{'hostId'}}[0];
120 &add_content2xml_hash($out_hash, "hostId", $hostId);
122 # Get productID
123 my $productId = @{$msg_hash->{'productId'}}[0];
124 &add_content2xml_hash($out_hash, "productId", $productId);
126 # Do an action request for all these -> "setup".
127 my $callobj = {
128 method => 'setProductActionRequest',
129 params => [ $productId, $hostId, "setup" ],
130 id => 1, };
131 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
133 if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
135 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
136 return ( &create_xml_string($out_hash) );
137 }
139 ################################
140 # @brief Deletes an Opsi-product from an Opsi-client.
141 # @param msg - STRING - xml message with tags hostId and productId
142 # @param msg_hash - HASHREF - message information parsed into a hash
143 # @param session_id - INTEGER - POE session id of the processing of this message
144 # @return out_msg - STRING - feedback to GOsa in success and error case
145 sub opsi_del_product_from_client {
146 my $startTime = Time::HiRes::time;
147 my ($msg, $msg_hash, $session_id) = @_;
148 my $header = @{$msg_hash->{'header'}}[0];
149 my $source = @{$msg_hash->{'source'}}[0];
150 my $target = @{$msg_hash->{'target'}}[0];
151 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
152 my ($hostId, $productId);
153 my $error = 0;
154 my ($sres, $sres_err, $sres_err_string);
156 # Build return message
157 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
158 if (defined $forward_to_gosa) {
159 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
160 }
162 # Sanity check of needed parameter
163 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
164 $error++;
165 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
166 &add_content2xml_hash($out_hash, "error", "hostId");
167 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
169 }
170 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
171 $error++;
172 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
173 &add_content2xml_hash($out_hash, "error", "productId");
174 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
175 }
177 # All parameter available
178 if (not $error) {
179 # Get hostId
180 $hostId = @{$msg_hash->{'hostId'}}[0];
181 &add_content2xml_hash($out_hash, "hostId", $hostId);
183 # Get productID
184 $productId = @{$msg_hash->{'productId'}}[0];
185 &add_content2xml_hash($out_hash, "productId", $productId);
187 # Check to get product action list
188 my $callobj = {
189 method => 'getPossibleProductActions_list',
190 params => [ $productId ],
191 id => 1, };
192 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
193 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
194 if ($sres_err){
195 &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
196 &add_content2xml_hash($out_hash, "error", $sres_err_string);
197 $error++;
198 }
199 }
201 # Check action uninstall of product
202 if (not $error) {
203 my $uninst_possible= 0;
204 foreach my $r (@{$sres->result}) {
205 if ($r eq 'uninstall') {
206 $uninst_possible= 1;
207 }
208 }
209 if (!$uninst_possible){
210 &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
211 &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
212 $error++;
213 }
214 }
216 # Set product state to "none"
217 # Do an action request for all these -> "setup".
218 if (not $error) {
219 my $callobj = {
220 method => 'setProductActionRequest',
221 params => [ $productId, $hostId, "none" ],
222 id => 1,
223 };
224 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
225 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
226 if ($sres_err){
227 &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
228 &add_content2xml_hash($out_hash, "error", $sres_err_string);
229 }
230 }
232 # Return message
233 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
234 return ( &create_xml_string($out_hash) );
235 }
237 ################################
238 # @brief Adds an Opsi client to Opsi.
239 # @param msg - STRING - xml message with tags hostId and macaddress
240 # @param msg_hash - HASHREF - message information parsed into a hash
241 # @param session_id - INTEGER - POE session id of the processing of this message
242 # @return out_msg - STRING - feedback to GOsa in success and error case
243 sub opsi_add_client {
244 my $startTime = Time::HiRes::time;
245 my ($msg, $msg_hash, $session_id) = @_;
246 my $header = @{$msg_hash->{'header'}}[0];
247 my $source = @{$msg_hash->{'source'}}[0];
248 my $target = @{$msg_hash->{'target'}}[0];
249 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
250 my ($hostId, $mac);
251 my $error = 0;
252 my ($sres, $sres_err, $sres_err_string);
254 # Build return message with twisted target and source
255 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
256 if (defined $forward_to_gosa) {
257 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
258 }
260 # Sanity check of needed parameter
261 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
262 $error++;
263 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
264 &add_content2xml_hash($out_hash, "error", "hostId");
265 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
266 }
267 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH')) {
268 $error++;
269 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
270 &add_content2xml_hash($out_hash, "error", "macaddress");
271 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
272 }
274 if (not $error) {
275 # Get hostId
276 $hostId = @{$msg_hash->{'hostId'}}[0];
277 &add_content2xml_hash($out_hash, "hostId", $hostId);
279 # Get macaddress
280 $mac = @{$msg_hash->{'macaddress'}}[0];
281 &add_content2xml_hash($out_hash, "macaddress", $mac);
283 my $name= $hostId;
284 $name=~ s/^([^.]+).*$/$1/;
285 my $domain= $hostId;
286 $domain=~ s/^[^.]+\.(.*)$/$1/;
287 my ($description, $notes, $ip);
289 if (defined @{$msg_hash->{'description'}}[0]){
290 $description = @{$msg_hash->{'description'}}[0];
291 }
292 if (defined @{$msg_hash->{'notes'}}[0]){
293 $notes = @{$msg_hash->{'notes'}}[0];
294 }
295 if (defined @{$msg_hash->{'ip'}}[0]){
296 $ip = @{$msg_hash->{'ip'}}[0];
297 }
299 my $callobj;
300 $callobj = {
301 method => 'createClient',
302 params => [ $name, $domain, $description, $notes, $ip, $mac ],
303 id => 1,
304 };
306 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
307 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
308 if ($sres_err){
309 &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
310 &add_content2xml_hash($out_hash, "error", $sres_err_string);
311 } else {
312 &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5);
313 }
314 }
316 # Return message
317 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
318 return ( &create_xml_string($out_hash) );
319 }
321 ################################
322 # @brief Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
323 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
324 # @param msg_hash - HASHREF - message information parsed into a hash
325 # @param session_id - INTEGER - POE session id of the processing of this message
326 # @return out_msg - STRING - feedback to GOsa in success and error case
327 sub opsi_modify_client {
328 my $startTime = Time::HiRes::time;
329 my ($msg, $msg_hash, $session_id) = @_;
330 my $header = @{$msg_hash->{'header'}}[0];
331 my $source = @{$msg_hash->{'source'}}[0];
332 my $target = @{$msg_hash->{'target'}}[0];
333 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
334 my $hostId;
335 my $error = 0;
336 my ($sres, $sres_err, $sres_err_string);
338 # Build return message with twisted target and source
339 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
340 if (defined $forward_to_gosa) {
341 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
342 }
344 # Sanity check of needed parameter
345 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
346 $error++;
347 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
348 &add_content2xml_hash($out_hash, "error", "hostId");
349 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
350 }
352 if (not $error) {
353 # Get hostId
354 $hostId = @{$msg_hash->{'hostId'}}[0];
355 &add_content2xml_hash($out_hash, "hostId", $hostId);
356 my $name= $hostId;
357 $name=~ s/^([^.]+).*$/$1/;
358 my $domain= $hostId;
359 $domain=~ s/^[^.]+(.*)$/$1/;
361 # Modify description, notes or mac if defined
362 my ($description, $notes, $mac);
363 my $callobj;
364 if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
365 $description = @{$msg_hash->{'description'}}[0];
366 $callobj = {
367 method => 'setHostDescription',
368 params => [ $hostId, $description ],
369 id => 1,
370 };
371 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
372 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
373 if ($sres_err){
374 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
375 &add_content2xml_hash($out_hash, "error", $sres_err_string);
376 }
377 }
378 if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
379 $notes = @{$msg_hash->{'notes'}}[0];
380 $callobj = {
381 method => 'setHostNotes',
382 params => [ $hostId, $notes ],
383 id => 1,
384 };
385 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
386 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
387 if ($sres_err){
388 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
389 &add_content2xml_hash($out_hash, "error", $sres_err_string);
390 }
391 }
392 if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
393 $mac = @{$msg_hash->{'mac'}}[0];
394 $callobj = {
395 method => 'setMacAddress',
396 params => [ $hostId, $mac ],
397 id => 1,
398 };
399 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
400 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
401 if ($sres_err){
402 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
403 &add_content2xml_hash($out_hash, "error", $sres_err_string);
404 }
405 }
406 }
408 # Return message
409 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
410 return ( &create_xml_string($out_hash) );
411 }
413 ################################
414 # @brief Get netboot products for specific host.
415 # @param msg - STRING - xml message with tag hostId
416 # @param msg_hash - HASHREF - message information parsed into a hash
417 # @param session_id - INTEGER - POE session id of the processing of this message
418 # @return out_msg - STRING - feedback to GOsa in success and error case
419 sub opsi_get_netboot_products {
420 my $startTime = Time::HiRes::time;
421 my ($msg, $msg_hash, $session_id) = @_;
422 my $header = @{$msg_hash->{'header'}}[0];
423 my $source = @{$msg_hash->{'source'}}[0];
424 my $target = @{$msg_hash->{'target'}}[0];
425 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
426 my $hostId;
428 # Build return message with twisted target and source
429 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
430 if (defined $forward_to_gosa) {
431 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
432 }
433 &add_content2xml_hash($out_hash, "xxx", "");
435 # Get hostId if defined
436 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
437 $hostId = @{$msg_hash->{'hostId'}}[0];
438 &add_content2xml_hash($out_hash, "hostId", $hostId);
439 }
441 # Move to XML string
442 my $xml_msg= &create_xml_string($out_hash);
444 my $callobj;
445 # Check if we need to get host or global information
446 if (defined $hostId){
447 $callobj = {
448 method => 'getProductHostInformation_list',
449 params => [ $hostId, undef, 'netboot'],
450 id => 1,
451 };
453 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
454 if (not &check_opsi_res($res)){
455 foreach my $product (@{$res->result}){
456 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>";
457 $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
458 }
459 }
461 } else {
463 # For hosts, only return the products that are or get installed
464 $callobj = {
465 method => 'getProductInformation_list',
466 params => [ undef, 'netboot' ],
467 id => 1,
468 };
470 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
471 if (not &check_opsi_res($res)){
472 foreach my $product (@{$res->result}) {
473 my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
474 $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
475 }
476 }
477 }
479 $xml_msg=~ s/<xxx><\/xxx>//;
481 # Retrun Message
482 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
483 return ( $xml_msg );
484 }
486 ################################
487 # @brief Get product properties for a product and a specific host or gobally for a product.
488 # @param msg - STRING - xml message with tags productId and optional hostId
489 # @param msg_hash - HASHREF - message information parsed into a hash
490 # @param session_id - INTEGER - POE session id of the processing of this message
491 # @return out_msg - STRING - feedback to GOsa in success and error case
492 sub opsi_get_product_properties {
493 my $startTime = Time::HiRes::time;
494 my ($msg, $msg_hash, $session_id) = @_;
495 my $header = @{$msg_hash->{'header'}}[0];
496 my $source = @{$msg_hash->{'source'}}[0];
497 my $target = @{$msg_hash->{'target'}}[0];
498 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
499 my ($hostId, $productId);
500 my $xml_msg;
502 # Build return message with twisted target and source
503 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
504 if (defined $forward_to_gosa) {
505 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
506 }
508 # Sanity check of needed parameter
509 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
510 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
511 &add_content2xml_hash($out_hash, "error", "productId");
512 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
514 # Return message
515 return ( &create_xml_string($out_hash) );
516 }
518 # Get productid
519 $productId = @{$msg_hash->{'productId'}}[0];
520 &add_content2xml_hash($out_hash, "producId", "$productId");
522 # Get hostId if defined
523 if (defined @{$msg_hash->{'hostId'}}[0]){
524 $hostId = @{$msg_hash->{'hostId'}}[0];
525 &add_content2xml_hash($out_hash, "hostId", $hostId);
526 }
528 # Load actions
529 my $callobj = {
530 method => 'getPossibleProductActions_list',
531 params => [ $productId ],
532 id => 1,
533 };
534 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
535 if (not &check_opsi_res($res)){
536 foreach my $action (@{$res->result}){
537 &add_content2xml_hash($out_hash, "action", $action);
538 }
539 }
541 # Add place holder
542 &add_content2xml_hash($out_hash, "xxx", "");
544 # Move to XML string
545 $xml_msg= &create_xml_string($out_hash);
547 # JSON Query
548 if (defined $hostId){
549 $callobj = {
550 method => 'getProductProperties_hash',
551 params => [ $productId, $hostId ],
552 id => 1,
553 };
554 } else {
555 $callobj = {
556 method => 'getProductProperties_hash',
557 params => [ $productId ],
558 id => 1,
559 };
560 }
561 $res = $main::opsi_client->call($main::opsi_url, $callobj);
563 # JSON Query 2
564 $callobj = {
565 method => 'getProductPropertyDefinitions_listOfHashes',
566 params => [ $productId ],
567 id => 1,
568 };
570 # Assemble options
571 my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
572 my $values = {};
573 my $descriptions = {};
574 if (not &check_opsi_res($res2)){
575 my $r= $res2->result;
577 foreach my $entr (@$r){
578 # Unroll values
579 my $cnv;
580 if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
581 foreach my $v (@{$entr->{'values'}}){
582 $cnv.= "<value>$v</value>";
583 }
584 } else {
585 $cnv= $entr->{'values'};
586 }
587 $values->{$entr->{'name'}}= $cnv;
588 $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
589 }
590 }
592 if (not &check_opsi_res($res)){
593 my $r= $res->result;
594 foreach my $key (keys %{$r}) {
595 my $item= "\n<item>";
596 my $value= $r->{$key};
597 my $dsc= "";
598 my $vals= "";
599 if (defined $descriptions->{$key}){
600 $dsc= $descriptions->{$key};
601 }
602 if (defined $values->{$key}){
603 $vals= $values->{$key};
604 }
605 $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
606 $item.= "</item>";
607 $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
608 }
609 }
611 $xml_msg=~ s/<xxx><\/xxx>//;
613 # Return message
614 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
615 return ( $xml_msg );
616 }
618 ################################
619 # @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.
620 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
621 # @param msg_hash - HASHREF - message information parsed into a hash
622 # @param session_id - INTEGER - POE session id of the processing of this message
623 # @return out_msg - STRING - feedback to GOsa in success and error case
624 sub opsi_set_product_properties {
625 my $startTime = Time::HiRes::time;
626 my ($msg, $msg_hash, $session_id) = @_;
627 my $header = @{$msg_hash->{'header'}}[0];
628 my $source = @{$msg_hash->{'source'}}[0];
629 my $target = @{$msg_hash->{'target'}}[0];
630 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
631 my ($productId, $hostId);
633 # Build return message with twisted target and source
634 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
635 if (defined $forward_to_gosa) {
636 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
637 }
639 # Sanity check of needed parameter
640 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
641 &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
642 &add_content2xml_hash($out_hash, "error", "productId");
643 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
644 return ( &create_xml_string($out_hash) );
645 }
646 if (not exists $msg_hash->{'item'}) {
647 &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
648 &add_content2xml_hash($out_hash, "error", "item");
649 &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1);
650 return ( &create_xml_string($out_hash) );
651 } else {
652 if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
653 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
654 &add_content2xml_hash($out_hash, "error", "name");
655 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1);
656 return ( &create_xml_string($out_hash) );
657 }
658 if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
659 &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
660 &add_content2xml_hash($out_hash, "error", "value");
661 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1);
662 return ( &create_xml_string($out_hash) );
663 }
664 }
665 # if no hostId is given, set_product_properties will act on globally
666 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1)) {
667 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
668 &add_content2xml_hash($out_hash, "error", "hostId");
669 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
670 return ( &create_xml_string($out_hash) );
671 }
674 # Get productId
675 $productId = @{$msg_hash->{'productId'}}[0];
676 &add_content2xml_hash($out_hash, "productId", $productId);
678 # Get hostId if defined
679 if (exists $msg_hash->{'hostId'}){
680 $hostId = @{$msg_hash->{'hostId'}}[0];
681 &add_content2xml_hash($out_hash, "hostId", $hostId);
682 }
684 # Set product states if requested
685 if (defined @{$msg_hash->{'action'}}[0]){
686 &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
687 }
688 if (defined @{$msg_hash->{'state'}}[0]){
689 &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
690 }
692 # Find properties
693 foreach my $item (@{$msg_hash->{'item'}}){
694 # JSON Query
695 my $callobj;
697 if (defined $hostId){
698 $callobj = {
699 method => 'setProductProperty',
700 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
701 id => 1,
702 };
703 } else {
704 $callobj = {
705 method => 'setProductProperty',
706 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
707 id => 1,
708 };
709 }
711 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
712 my ($res_err, $res_err_string) = &check_opsi_res($res);
714 if ($res_err){
715 &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
716 &add_content2xml_hash($out_hash, "error", $res_err_string);
717 }
718 }
721 # Return message
722 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
723 return ( &create_xml_string($out_hash) );
724 }
726 ################################
727 # @brief Reports client hardware inventory.
728 # @param msg - STRING - xml message with tag hostId
729 # @param msg_hash - HASHREF - message information parsed into a hash
730 # @param session_id - INTEGER - POE session id of the processing of this message
731 # @return out_msg - STRING - feedback to GOsa in success and error case
732 sub opsi_get_client_hardware {
733 my $startTime = Time::HiRes::time;
734 my ($msg, $msg_hash, $session_id) = @_;
735 my $header = @{$msg_hash->{'header'}}[0];
736 my $source = @{$msg_hash->{'source'}}[0];
737 my $target = @{$msg_hash->{'target'}}[0];
738 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
739 my $hostId;
740 my $error = 0;
741 my $xml_msg;
743 # Sanity check of needed parameter
744 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
745 $hostId = @{$msg_hash->{'hostId'}}[0];
746 } else {
747 return &_giveErrorFeedback($msg_hash, $_, $session_id);
748 }
751 # Build return message with twisted target and source
752 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
753 if (defined $forward_to_gosa) {
754 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
755 }
756 &add_content2xml_hash($out_hash, "hostId", "$hostId");
757 &add_content2xml_hash($out_hash, "xxx", "");
759 # Move to XML string
760 $xml_msg= &create_xml_string($out_hash);
762 my $res = &_callOpsi(method=>'getHardwareInformation_hash', params=>[ $hostId ]);
763 if (not &check_opsi_res($res)){
764 my $result= $res->result;
765 if (ref $result eq "HASH") {
766 foreach my $r (keys %{$result}){
767 my $item= "\n<item><id>".xml_quote($r)."</id>";
768 my $value= $result->{$r};
769 foreach my $sres (@{$value}){
771 foreach my $dres (keys %{$sres}){
772 if (defined $sres->{$dres}){
773 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
774 }
775 }
777 }
778 $item.= "</item>";
779 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
781 }
782 }
783 }
785 $xml_msg=~ s/<xxx><\/xxx>//;
787 # Return message
788 my $endTime = Time::HiRes::time;
789 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
790 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
791 return ( $xml_msg );
792 }
794 ################################
795 # @brief Reports all Opsi clients.
796 # @param msg - STRING - xml message
797 # @param msg_hash - HASHREF - message information parsed into a hash
798 # @param session_id - INTEGER - POE session id of the processing of this message
799 # @return out_msg - STRING - feedback to GOsa in success and error case
800 sub opsi_list_clients {
801 my $startTime = Time::HiRes::time;
802 my ($msg, $msg_hash, $session_id) = @_;
803 my $header = @{$msg_hash->{'header'}}[0];
804 my $source = @{$msg_hash->{'source'}}[0];
805 my $target = @{$msg_hash->{'target'}}[0];
806 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
808 # Build return message with twisted target and source
809 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
810 if (defined $forward_to_gosa) {
811 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
812 }
813 &add_content2xml_hash($out_hash, "xxx", "");
815 # Move to XML string
816 my $xml_msg= &create_xml_string($out_hash);
818 # JSON Query
819 my $callobj = {
820 method => 'getClientsInformation_listOfHashes',
821 params => [ ],
822 id => 1,
823 };
825 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
826 if (not &check_opsi_res($res)){
827 foreach my $host (@{$res->result}){
828 my $item= "\n<item><name>".$host->{'hostId'}."</name>";
829 $item.= "<mac>".xml_quote($host->{'macAddress'})."</mac>";
830 if (defined($host->{'description'})){
831 $item.= "<description>".xml_quote($host->{'description'})."</description>";
832 }
833 if (defined($host->{'notes'})){
834 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
835 }
836 if (defined($host->{'lastSeen'})){
837 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
838 }
840 $item.= "</item>";
841 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
842 }
843 }
844 $xml_msg=~ s/<xxx><\/xxx>//;
846 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
847 return ( $xml_msg );
848 }
850 ################################
851 # @brief Reports client software inventory.
852 # @param msg - STRING - xml message with tag hostId
853 # @param msg_hash - HASHREF - message information parsed into a hash
854 # @param session_id - INTEGER - POE session id of the processing of this message
855 # @return out_msg - STRING - feedback to GOsa in success and error case
856 sub opsi_get_client_software {
857 my $startTime = Time::HiRes::time;
858 my ($msg, $msg_hash, $session_id) = @_;
859 my $header = @{$msg_hash->{'header'}}[0];
860 my $source = @{$msg_hash->{'source'}}[0];
861 my $target = @{$msg_hash->{'target'}}[0];
862 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
863 my $error = 0;
864 my $hostId;
865 my $xml_msg;
867 # Build return message with twisted target and source
868 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
869 if (defined $forward_to_gosa) {
870 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
871 }
873 # Sanity check of needed parameter
874 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
875 $error++;
876 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
877 &add_content2xml_hash($out_hash, "error", "hostId");
878 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
879 }
881 if (not $error) {
883 # Get hostId
884 $hostId = @{$msg_hash->{'hostId'}}[0];
885 &add_content2xml_hash($out_hash, "hostId", "$hostId");
886 &add_content2xml_hash($out_hash, "xxx", "");
887 }
889 $xml_msg= &create_xml_string($out_hash);
891 if (not $error) {
893 # JSON Query
894 my $callobj = {
895 method => 'getSoftwareInformation_hash',
896 params => [ $hostId ],
897 id => 1,
898 };
900 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
901 if (not &check_opsi_res($res)){
902 my $result= $res->result;
903 }
905 $xml_msg=~ s/<xxx><\/xxx>//;
907 }
909 # Return message
910 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
911 return ( $xml_msg );
912 }
914 ################################
915 # @brief Reports product for given hostId or globally.
916 # @param msg - STRING - xml message with optional tag hostId
917 # @param msg_hash - HASHREF - message information parsed into a hash
918 # @param session_id - INTEGER - POE session id of the processing of this message
919 # @return out_msg - STRING - feedback to GOsa in success and error case
920 sub opsi_get_local_products {
921 my $startTime = Time::HiRes::time;
922 my ($msg, $msg_hash, $session_id) = @_;
923 my $header = @{$msg_hash->{'header'}}[0];
924 my $source = @{$msg_hash->{'source'}}[0];
925 my $target = @{$msg_hash->{'target'}}[0];
926 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
927 my $hostId;
929 # Build return message with twisted target and source
930 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
931 if (defined $forward_to_gosa) {
932 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
933 }
934 &add_content2xml_hash($out_hash, "xxx", "");
936 # Get hostId if defined
937 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
938 $hostId = @{$msg_hash->{'hostId'}}[0];
939 &add_content2xml_hash($out_hash, "hostId", $hostId);
940 }
942 # Move to XML string
943 my $xml_msg= &create_xml_string($out_hash);
945 my $callobj;
946 # Check if we need to get host or global information
947 if (defined $hostId){
948 $callobj = {
949 method => 'getProductHostInformation_list',
950 params => [ $hostId ],
951 id => 1,
952 };
954 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
955 if (not &check_opsi_res($res)){
956 foreach my $product (@{$res->result}){
957 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>";
958 $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
959 }
960 }
962 } else {
964 # For hosts, only return the products that are or get installed
965 $callobj = {
966 method => 'getLocalBootProductInformation_list',
967 params => [ ],
968 id => 1,
969 };
971 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
972 if (not &check_opsi_res($res)){
973 foreach my $product (@{$res->result}) {
974 my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
975 $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
976 }
977 }
978 }
980 $xml_msg=~ s/<xxx><\/xxx>//;
982 # Retrun Message
983 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
984 return ( $xml_msg );
985 }
987 ################################
988 # @brief Deletes a client from Opsi.
989 # @param msg - STRING - xml message with tag hostId
990 # @param msg_hash - HASHREF - message information parsed into a hash
991 # @param session_id - INTEGER - POE session id of the processing of this message
992 # @return out_msg - STRING - feedback to GOsa in success and error case
993 sub opsi_del_client {
994 my $startTime = Time::HiRes::time;
995 my ($msg, $msg_hash, $session_id) = @_;
996 my $header = @{$msg_hash->{'header'}}[0];
997 my $source = @{$msg_hash->{'source'}}[0];
998 my $target = @{$msg_hash->{'target'}}[0];
999 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1000 my $hostId;
1001 my $error = 0;
1003 # Build return message with twisted target and source
1004 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1005 if (defined $forward_to_gosa) {
1006 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1007 }
1009 # Sanity check of needed parameter
1010 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1011 $error++;
1012 &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1013 &add_content2xml_hash($out_hash, "error", "hostId");
1014 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
1015 }
1017 if (not $error) {
1019 # Get hostId
1020 $hostId = @{$msg_hash->{'hostId'}}[0];
1021 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1023 # JSON Query
1024 my $callobj = {
1025 method => 'deleteClient',
1026 params => [ $hostId ],
1027 id => 1,
1028 };
1029 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1030 }
1032 # Move to XML string
1033 my $xml_msg= &create_xml_string($out_hash);
1035 # Return message
1036 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1037 return ( $xml_msg );
1038 }
1040 ################################
1041 # @brief Set a client in Opsi to install and trigger a wake on lan message (WOL).
1042 # @param msg - STRING - xml message with tags hostId, macaddress
1043 # @param msg_hash - HASHREF - message information parsed into a hash
1044 # @param session_id - INTEGER - POE session id of the processing of this message
1045 # @return out_msg - STRING - feedback to GOsa in success and error case
1046 sub opsi_install_client {
1047 my $startTime = Time::HiRes::time;
1048 my ($msg, $msg_hash, $session_id) = @_;
1049 my $header = @{$msg_hash->{'header'}}[0];
1050 my $source = @{$msg_hash->{'source'}}[0];
1051 my $target = @{$msg_hash->{'target'}}[0];
1052 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1053 my ($hostId, $macaddress);
1054 my $error = 0;
1055 my @out_msg_l;
1057 # Build return message with twisted target and source
1058 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1059 if (defined $forward_to_gosa) {
1060 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1061 }
1063 # Sanity check of needed parameter
1064 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH')) {
1065 $error++;
1066 &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1067 &add_content2xml_hash($out_hash, "error", "hostId");
1068 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
1069 }
1070 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') ) {
1071 $error++;
1072 &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1073 &add_content2xml_hash($out_hash, "error", "macaddress");
1074 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
1075 } else {
1076 if ((exists $msg_hash->{'macaddress'}) &&
1077 ($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)) {
1078 $macaddress = $1;
1079 } else {
1080 $error ++;
1081 &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1082 &add_content2xml_hash($out_hash, "error", "macaddress");
1083 &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1);
1084 }
1085 }
1087 if (not $error) {
1089 # Get hostId
1090 $hostId = @{$msg_hash->{'hostId'}}[0];
1091 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1093 # Load all products for this host with status != "not_installed" or actionRequest != "none"
1094 my $callobj = {
1095 method => 'getProductStates_hash',
1096 params => [ $hostId ],
1097 id => 1,
1098 };
1100 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1101 if (not &check_opsi_res($hres)){
1102 my $htmp= $hres->result->{$hostId};
1104 # check state != not_installed or action == setup -> load and add
1105 foreach my $product (@{$htmp}){
1106 # Now we've a couple of hashes...
1107 if ($product->{'installationStatus'} ne "not_installed" or
1108 $product->{'actionRequest'} ne "none"){
1110 # Do an action request for all these -> "setup".
1111 $callobj = {
1112 method => 'setProductActionRequest',
1113 params => [ $product->{'productId'}, $hostId, "setup" ],
1114 id => 1,
1115 };
1116 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1117 my ($res_err, $res_err_string) = &check_opsi_res($res);
1118 if ($res_err){
1119 &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1120 } else {
1121 &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1122 }
1123 }
1124 }
1125 }
1126 push(@out_msg_l, &create_xml_string($out_hash));
1129 # Build wakeup message for client
1130 if (not $error) {
1131 my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1132 &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1133 my $wakeup_msg = &create_xml_string($wakeup_hash);
1134 push(@out_msg_l, $wakeup_msg);
1136 # invoke trigger wake for this gosa-si-server
1137 &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1138 }
1139 }
1141 # Return messages
1142 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1143 return @out_msg_l;
1144 }
1146 ################################
1147 # @brief Set action for an Opsi client
1148 # @param product - STRING - Opsi product
1149 # @param action - STRING - action
1150 # @param hostId - STRING - Opsi hostId
1151 sub _set_action {
1152 my $product= shift;
1153 my $action = shift;
1154 my $hostId = shift;
1155 my $callobj;
1157 $callobj = {
1158 method => 'setProductActionRequest',
1159 params => [ $product, $hostId, $action],
1160 id => 1,
1161 };
1163 $main::opsi_client->call($main::opsi_url, $callobj);
1164 }
1166 ################################
1167 # @brief Set state for an Opsi client
1168 # @param product - STRING - Opsi product
1169 # @param action - STRING - state
1170 # @param hostId - STRING - Opsi hostId
1171 sub _set_state {
1172 my $product = shift;
1173 my $state = shift;
1174 my $hostId = shift;
1175 my $callobj;
1177 $callobj = {
1178 method => 'setProductState',
1179 params => [ $product, $hostId, $state ],
1180 id => 1,
1181 };
1183 $main::opsi_client->call($main::opsi_url, $callobj);
1184 }
1186 ################################
1187 # @brief Create a license pool at Opsi server.
1188 # @param licensePoolId The name of the pool (optional).
1189 # @param description The description of the pool (optional).
1190 # @param productIds A list of assigned porducts of the pool (optional).
1191 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional).
1192 sub opsi_createLicensePool {
1193 my $startTime = Time::HiRes::time;
1194 my ($msg, $msg_hash, $session_id) = @_;
1195 my $header = @{$msg_hash->{'header'}}[0];
1196 my $source = @{$msg_hash->{'source'}}[0];
1197 my $target = @{$msg_hash->{'target'}}[0];
1198 my $out_hash;
1199 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1200 my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1201 my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1202 my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1204 # Create license Pool
1205 my $callobj = {
1206 method => 'createLicensePool',
1207 params => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1208 id => 1,
1209 };
1210 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1212 # Check Opsi error
1213 my ($res_error, $res_error_str) = &check_opsi_res($res);
1214 if ($res_error){
1215 # Create error message
1216 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1217 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1218 return ( &create_xml_string($out_hash) );
1219 }
1221 # Create function result message
1222 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1223 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1225 my $endTime = Time::HiRes::time;
1226 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1227 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1228 return ( &create_xml_string($out_hash) );
1229 }
1231 ################################
1232 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1233 sub opsi_getLicensePools_listOfHashes {
1234 my $startTime = Time::HiRes::time;
1235 my ($msg, $msg_hash, $session_id) = @_;
1236 my $header = @{$msg_hash->{'header'}}[0];
1237 my $source = @{$msg_hash->{'source'}}[0];
1238 my $out_hash;
1240 # Fetch infos from Opsi server
1241 my $callobj = {
1242 method => 'getLicensePools_listOfHashes',
1243 params => [ ],
1244 id => 1,
1245 };
1246 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1248 # Check Opsi error
1249 my ($res_error, $res_error_str) = &check_opsi_res($res);
1250 if ($res_error){
1251 # Create error message
1252 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1253 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1254 return ( &create_xml_string($out_hash) );
1255 }
1257 # Create function result message
1258 my $res_hash = { 'hit'=> [] };
1259 foreach my $licensePool ( @{$res->result}) {
1260 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1261 'description' => [$licensePool->{'description'}],
1262 'productIds' => $licensePool->{'productIds'},
1263 'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1264 };
1265 push( @{$res_hash->{hit}}, $licensePool_hash );
1266 }
1268 # Create function result message
1269 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1270 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1271 $out_hash->{result} = [$res_hash];
1273 my $endTime = Time::HiRes::time;
1274 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1275 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1276 return ( &create_xml_string($out_hash) );
1277 }
1279 ################################
1280 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1281 # @param licensePoolId The name of the pool.
1282 sub opsi_getLicensePool_hash {
1283 my $startTime = Time::HiRes::time;
1284 my ($msg, $msg_hash, $session_id) = @_;
1285 my $header = @{$msg_hash->{'header'}}[0];
1286 my $source = @{$msg_hash->{'source'}}[0];
1287 my $target = @{$msg_hash->{'target'}}[0];
1288 my $licensePoolId;
1289 my $out_hash;
1291 # Check input sanity
1292 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1293 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1294 } else {
1295 return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
1296 }
1298 # Fetch infos from Opsi server
1299 my $callobj = {
1300 method => 'getLicensePool_hash',
1301 params => [ $licensePoolId ],
1302 id => 1,
1303 };
1304 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1306 # Check Opsi error
1307 my ($res_error, $res_error_str) = &check_opsi_res($res);
1308 if ($res_error){
1309 # Create error message
1310 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1311 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1312 &add_content2xml_hash($out_hash, "error", $res_error_str);
1313 return ( &create_xml_string($out_hash) );
1314 }
1316 # Create function result message
1317 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1318 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1319 &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1320 &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1321 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1322 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1324 my $endTime = Time::HiRes::time;
1325 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1326 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1327 return ( &create_xml_string($out_hash) );
1328 }
1330 sub _parse_getSoftwareLicenseUsages {
1331 my $res = shift;
1333 # Parse Opsi result
1334 my $tmp_licensePool_cache = {};
1335 my $res_hash = { 'hit'=> [] };
1336 foreach my $license ( @{$res}) {
1337 my $tmp_licensePool = $license->{'licensePoolId'};
1338 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1339 # Fetch missing informations from Opsi and cache the results for a possible later usage
1340 my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1341 if (not $err) {
1342 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1343 }
1344 }
1345 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1346 'notes' => [$license->{'notes'}],
1347 'licenseKey' => [$license->{'licenseKey'}],
1348 'hostId' => [$license->{'hostId'}],
1349 'licensePoolId' => [$tmp_licensePool],
1350 };
1351 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1352 $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1353 map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1354 map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1355 }
1356 push( @{$res_hash->{hit}}, $license_hash );
1357 }
1359 return $res_hash;
1360 }
1362 ################################
1363 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1364 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1365 # @param licensePoolId The name of the pool (optional).
1366 sub opsi_getSoftwareLicenseUsages {
1367 my $startTime = Time::HiRes::time;
1368 my ($msg, $msg_hash, $session_id) = @_;
1369 my $header = @{$msg_hash->{'header'}}[0];
1370 my $source = @{$msg_hash->{'source'}}[0];
1371 my $target = @{$msg_hash->{'target'}}[0];
1372 my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1373 my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1374 my $out_hash;
1376 my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1377 if ($err){
1378 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1379 }
1381 # Parse Opsi result
1382 my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1384 # Create function result message
1385 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1386 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1387 $out_hash->{result} = [$res_hash];
1389 my $endTime = Time::HiRes::time;
1390 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1391 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1392 return ( &create_xml_string($out_hash) );
1393 }
1395 ################################
1396 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1397 # @param productId Something like 'firefox', 'python' or anything else .
1398 sub opsi_getSoftwareLicenseUsagesForProductId {
1399 my $startTime = Time::HiRes::time;
1400 my ($msg, $msg_hash, $session_id) = @_;
1401 my $header = @{$msg_hash->{'header'}}[0];
1402 my $source = @{$msg_hash->{'source'}}[0];
1404 # Check input sanity
1405 my $productId;
1406 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1407 $productId= @{$msg_hash->{'productId'}}[0];
1408 } else {
1409 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1410 }
1412 # Fetch licensePoolId for productId
1413 my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1414 if ($err){
1415 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1416 }
1418 my $licensePoolId;
1420 # Fetch softwareLiceceUsages for licensePoolId
1421 ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1422 if ($err){
1423 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1424 }
1426 # Parse Opsi result
1427 my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1429 # Create function result message
1430 my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
1431 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1432 $out_hash->{result} = [$res_hash];
1434 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1435 return ( &create_xml_string($out_hash) );
1436 }
1438 ################################
1439 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1440 # @param softwareLicenseId Identificator of a license.
1441 sub opsi_getSoftwareLicense_hash {
1442 my $startTime = Time::HiRes::time;
1443 my ($msg, $msg_hash, $session_id) = @_;
1444 my $header = @{$msg_hash->{'header'}}[0];
1445 my $source = @{$msg_hash->{'source'}}[0];
1446 my $target = @{$msg_hash->{'target'}}[0];
1447 my $softwareLicenseId;
1448 my $out_hash;
1450 # Check input sanity
1451 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1452 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1453 } else {
1454 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1455 }
1457 my $callobj = {
1458 method => 'getSoftwareLicense_hash',
1459 params => [ $softwareLicenseId ],
1460 id => 1,
1461 };
1462 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1464 # Check Opsi error
1465 my ($res_error, $res_error_str) = &check_opsi_res($res);
1466 if ($res_error){
1467 # Create error message
1468 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1469 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1470 return ( &create_xml_string($out_hash) );
1471 }
1473 # Create function result message
1474 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1475 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1476 &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1477 &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1478 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1479 &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1480 foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1481 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1482 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1483 }
1485 my $endTime = Time::HiRes::time;
1486 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1487 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1488 return ( &create_xml_string($out_hash) );
1489 }
1491 ################################
1492 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool.
1493 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted.
1494 # @param licensePoolId The name of the pool.
1495 sub opsi_deleteLicensePool {
1496 my $startTime = Time::HiRes::time;
1497 my ($msg, $msg_hash, $session_id) = @_;
1498 my $header = @{$msg_hash->{'header'}}[0];
1499 my $source = @{$msg_hash->{'source'}}[0];
1500 my $target = @{$msg_hash->{'target'}}[0];
1501 my $licensePoolId;
1502 my $out_hash;
1504 # Check input sanity
1505 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1506 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1507 } else {
1508 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1509 }
1511 # Fetch softwareLicenseIds used in license pool
1512 # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1513 # but not the license contracts of the software licenses. In our case each software license has exactly one license contract.
1514 my $callobj = {
1515 method => 'getSoftwareLicenses_listOfHashes',
1516 params => [ ],
1517 id => 1,
1518 };
1519 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1521 # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1522 my @lCI_toBeDeleted;
1523 foreach my $softwareLicenseHash ( @{$res->result} ) {
1524 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) {
1525 next;
1526 }
1527 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1528 }
1530 # Delete license pool at Opsi server
1531 $callobj = {
1532 method => 'deleteLicensePool',
1533 params => [ $licensePoolId, 'deleteLicenses=True' ],
1534 id => 1,
1535 };
1536 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1537 my ($res_error, $res_error_str) = &check_opsi_res($res);
1538 if ($res_error){
1539 # Create error message
1540 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1541 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1542 return ( &create_xml_string($out_hash) );
1543 }
1545 # Delete each license contract connected with the license pool
1546 foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1547 my $callobj = {
1548 method => 'deleteLicenseContract',
1549 params => [ $licenseContractId ],
1550 id => 1,
1551 };
1552 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1553 my ($res_error, $res_error_str) = &check_opsi_res($res);
1554 if ($res_error){
1555 # Create error message
1556 &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1557 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1558 return ( &create_xml_string($out_hash) );
1559 }
1560 }
1562 # Create function result message
1563 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1564 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1566 my $endTime = Time::HiRes::time;
1567 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1568 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1569 return ( &create_xml_string($out_hash) );
1570 }
1572 ################################
1573 # @brief Create a license contract, create a software license and add the software license to the license pool
1574 # @param licensePoolId The name of the pool the license should be assigned.
1575 # @param licenseKey The license key.
1576 # @param partner Name of the license partner (optional).
1577 # @param conclusionDate Date of conclusion of license contract (optional)
1578 # @param notificationDate Date of notification that license is running out soon (optional).
1579 # @param notes This is the place for some notes (optional)
1580 # @param softwareLicenseId Identificator of a license (optional).
1581 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1582 # @param maxInstallations The number of clients use this license (optional).
1583 # @param boundToHost The name of the client the license is bound to (optional).
1584 # @param expirationDate The date when the license is running down (optional).
1585 sub opsi_createLicense {
1586 my $startTime = Time::HiRes::time;
1587 my ($msg, $msg_hash, $session_id) = @_;
1588 my $header = @{$msg_hash->{'header'}}[0];
1589 my $source = @{$msg_hash->{'source'}}[0];
1590 my $target = @{$msg_hash->{'target'}}[0];
1591 my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1592 my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1593 my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1594 my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1595 my $licenseContractId = undef;
1596 my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1597 my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1598 my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1599 my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1600 my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1601 my $licensePoolId;
1602 my $licenseKey;
1603 my $out_hash;
1605 # Check input sanity
1606 if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1607 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1608 } else {
1609 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1610 }
1611 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1612 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1613 } else {
1614 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1615 }
1616 if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1617 return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
1618 }
1620 # Automatically define licenseContractId if ID is not given
1621 if (defined $softwareLicenseId) {
1622 $licenseContractId = "c_".$softwareLicenseId;
1623 }
1625 # Create license contract at Opsi server
1626 my $callobj = {
1627 method => 'createLicenseContract',
1628 params => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1629 id => 1,
1630 };
1631 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1633 # Check Opsi error
1634 my ($res_error, $res_error_str) = &check_opsi_res($res);
1635 if ($res_error){
1636 # Create error message
1637 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1638 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1639 return ( &create_xml_string($out_hash) );
1640 }
1642 $licenseContractId = $res->result;
1644 # Create software license at Opsi server
1645 $callobj = {
1646 method => 'createSoftwareLicense',
1647 params => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1648 id => 1,
1649 };
1650 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1652 # Check Opsi error
1653 ($res_error, $res_error_str) = &check_opsi_res($res);
1654 if ($res_error){
1655 # Create error message
1656 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1657 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1658 return ( &create_xml_string($out_hash) );
1659 }
1661 $softwareLicenseId = $res->result;
1663 # Add software license to license pool
1664 $callobj = {
1665 method => 'addSoftwareLicenseToLicensePool',
1666 params => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1667 id => 1,
1668 };
1669 $res = $main::opsi_client->call($main::opsi_url, $callobj);
1671 # Check Opsi error
1672 ($res_error, $res_error_str) = &check_opsi_res($res);
1673 if ($res_error){
1674 # Create error message
1675 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1676 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1677 return ( &create_xml_string($out_hash) );
1678 }
1680 # Create function result message
1681 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1682 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1684 my $endTime = Time::HiRes::time;
1685 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1686 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1687 return ( &create_xml_string($out_hash) );
1688 }
1690 ################################
1691 # @brief Assign a software license to a host
1692 # @param hostid Something like client_1.intranet.mydomain.de
1693 # @param licensePoolId The name of the pool.
1694 sub opsi_assignSoftwareLicenseToHost {
1695 my $startTime = Time::HiRes::time;
1696 my ($msg, $msg_hash, $session_id) = @_;
1697 my $header = @{$msg_hash->{'header'}}[0];
1698 my $source = @{$msg_hash->{'source'}}[0];
1699 my $target = @{$msg_hash->{'target'}}[0];
1700 my $hostId;
1701 my $licensePoolId;
1703 # Check input sanity
1704 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1705 $hostId = @{$msg_hash->{'hostId'}}[0];
1706 } else {
1707 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1708 }
1709 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1710 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1711 } else {
1712 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1713 }
1715 # Assign a software license to a host
1716 my $callobj = {
1717 method => 'getAndAssignSoftwareLicenseKey',
1718 params => [ $hostId, $licensePoolId ],
1719 id => 1,
1720 };
1721 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1723 # Check Opsi error
1724 my ($res_error, $res_error_str) = &check_opsi_res($res);
1725 if ($res_error){
1726 # Create error message
1727 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1728 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1729 return ( &create_xml_string($out_hash) );
1730 }
1732 # Create function result message
1733 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1734 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1736 my $endTime = Time::HiRes::time;
1737 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1738 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1739 return ( &create_xml_string($out_hash) );
1740 }
1742 ################################
1743 # @brief Unassign a software license from a host.
1744 # @param hostid Something like client_1.intranet.mydomain.de
1745 # @param licensePoolId The name of the pool.
1746 sub opsi_unassignSoftwareLicenseFromHost {
1747 my $startTime = Time::HiRes::time;
1748 my ($msg, $msg_hash, $session_id) = @_;
1749 my $header = @{$msg_hash->{'header'}}[0];
1750 my $source = @{$msg_hash->{'source'}}[0];
1751 my $target = @{$msg_hash->{'target'}}[0];
1752 my $hostId;
1753 my $licensePoolId;
1755 # Check input sanity
1756 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1757 $hostId = @{$msg_hash->{'hostId'}}[0];
1758 } else {
1759 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1760 }
1761 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1762 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1763 } else {
1764 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1765 }
1767 # Unassign a software license from a host
1768 my $callobj = {
1769 method => 'deleteSoftwareLicenseUsage',
1770 params => [ $hostId, '', $licensePoolId ],
1771 id => 1,
1772 };
1773 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1775 # Check Opsi error
1776 my ($res_error, $res_error_str) = &check_opsi_res($res);
1777 if ($res_error){
1778 # Create error message
1779 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1780 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1781 return ( &create_xml_string($out_hash) );
1782 }
1784 # Create function result message
1785 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1786 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1788 my $endTime = Time::HiRes::time;
1789 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1790 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1791 return ( &create_xml_string($out_hash) );
1792 }
1794 ################################
1795 # @brief Unassign all software licenses from a host
1796 # @param hostid Something like client_1.intranet.mydomain.de
1797 sub opsi_unassignAllSoftwareLicensesFromHost {
1798 my $startTime = Time::HiRes::time;
1799 my ($msg, $msg_hash, $session_id) = @_;
1800 my $header = @{$msg_hash->{'header'}}[0];
1801 my $source = @{$msg_hash->{'source'}}[0];
1802 my $target = @{$msg_hash->{'target'}}[0];
1803 my $hostId;
1805 # Check input sanity
1806 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1807 $hostId = @{$msg_hash->{'hostId'}}[0];
1808 } else {
1809 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1810 }
1812 # Unassign all software licenses from a host
1813 my $callobj = {
1814 method => 'deleteAllSoftwareLicenseUsages',
1815 params => [ $hostId ],
1816 id => 1,
1817 };
1818 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1820 # Check Opsi error
1821 my ($res_error, $res_error_str) = &check_opsi_res($res);
1822 if ($res_error){
1823 # Create error message
1824 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1825 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1826 return ( &create_xml_string($out_hash) );
1827 }
1829 # Create function result message
1830 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1831 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1833 my $endTime = Time::HiRes::time;
1834 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
1835 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
1836 return ( &create_xml_string($out_hash) );
1837 }
1840 ################################
1841 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1842 # and the number of max and remaining installations for a given OPSI product.
1843 # @param productId Identificator of an OPSI product.
1844 sub opsi_getLicenseInformationForProduct {
1845 my $startTime = Time::HiRes::time;
1846 my ($msg, $msg_hash, $session_id) = @_;
1847 my $header = @{$msg_hash->{'header'}}[0];
1848 my $source = @{$msg_hash->{'source'}}[0];
1849 my $productId;
1850 my $out_hash;
1852 # Check input sanity
1853 if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1854 $productId = @{$msg_hash->{'productId'}}[0];
1855 } else {
1856 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1857 }
1859 # Fetch infos from Opsi server
1860 my $callobj = {
1861 method => 'getLicensePoolId',
1862 params => [ $productId ],
1863 id => 1,
1864 };
1865 #my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1866 my $res = $opsi_client->call($opsi_url, $callobj);
1868 # Check Opsi error
1869 my ($res_error, $res_error_str) = &check_opsi_res($res);
1870 if ($res_error){
1871 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
1872 }
1874 my $licensePoolId = $res->result;
1876 # Fetch statistic information for given pool ID
1877 $callobj = {
1878 method => 'getLicenseStatistics_hash',
1879 params => [ ],
1880 id => 1,
1881 };
1882 $res = $opsi_client->call($opsi_url, $callobj);
1884 # Check Opsi error
1885 ($res_error, $res_error_str) = &check_opsi_res($res);
1886 if ($res_error){
1887 # Create error message
1888 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
1889 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1890 return ( &create_xml_string($out_hash) );
1891 }
1893 # Create function result message
1894 $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1895 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1896 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1897 &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
1898 &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
1899 &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
1900 &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
1901 map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
1903 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
1904 return ( &create_xml_string($out_hash) );
1905 }
1908 ################################
1909 # @brief Returns licensePoolId, description, a list of productIds, al list of windowsSoftwareIds and a list of licenses for a given licensePoolId.
1910 # Each license contains softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseKeys, hostIds, expirationDate, boundToHost and licenseContractId.
1911 # The licenseContract contains conclusionDate, expirationDate, notes, notificationDate and partner.
1912 # @param licensePoolId The name of the pool.
1913 sub opsi_getPool {
1914 my $startTime = Time::HiRes::time;
1915 my ($msg, $msg_hash, $session_id) = @_;
1916 my $header = @{$msg_hash->{'header'}}[0];
1917 my $source = @{$msg_hash->{'source'}}[0];
1919 # Check input sanity
1920 my $licensePoolId;
1921 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1922 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1923 } else {
1924 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1925 }
1927 # Create hash for the answer
1928 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1929 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
1931 # Call Opsi
1932 my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
1933 if ($err){
1934 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
1935 }
1936 # Add data to outgoing hash
1937 &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
1938 &add_content2xml_hash($out_hash, "description", $res->{'description'});
1939 map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
1940 map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
1943 # Call Opsi two times
1944 my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1945 if ($usages_err){
1946 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
1947 }
1948 my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
1949 if ($licenses_err){
1950 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
1951 }
1953 # Add data to outgoing hash
1954 # Parse through all software licenses and select those associated to the pool
1955 my $res_hash = { 'hit'=> [] };
1956 foreach my $license ( @$licenses_res) {
1957 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
1958 my $found = 0;
1959 my @licensePoolIds_list = @{$license->{licensePoolIds}};
1960 foreach my $lPI ( @licensePoolIds_list) {
1961 if ($lPI eq $licensePoolId) { $found++ }
1962 }
1963 if (not $found ) { next; };
1964 # Found matching licensePoolId
1965 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1966 'licenseKeys' => {},
1967 'expirationDate' => [$license->{'expirationDate'}],
1968 'boundToHost' => [$license->{'boundToHost'}],
1969 'maxInstallations' => [$license->{'maxInstallations'}],
1970 'licenseType' => [$license->{'licenseType'}],
1971 'licenseContractId' => [$license->{'licenseContractId'}],
1972 'licensePoolIds' => [],
1973 'hostIds' => [],
1974 };
1975 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
1976 push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
1977 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
1978 }
1979 foreach my $usage (@$usages_res) {
1980 # Search for hostIds with matching softwareLicenseId
1981 if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
1982 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
1983 }
1984 }
1986 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
1987 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
1988 if ($lContract_err){
1989 return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
1990 }
1991 $license_hash->{$license->{'licenseContractId'}} = [];
1992 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
1993 'notificationDate' => [$lContract_res->{notificationDate}],
1994 'notes' => [$lContract_res->{notes}],
1995 'exirationDate' => [$lContract_res->{expirationDate}],
1996 'partner' => [$lContract_res->{partner}],
1997 };
1998 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2000 push( @{$res_hash->{hit}}, $license_hash );
2001 }
2002 $out_hash->{licenses} = [$res_hash];
2004 my $endTime = Time::HiRes::time;
2005 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2006 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2007 return ( &create_xml_string($out_hash) );
2008 }
2011 ################################
2012 # @brief Removes at first the software license from license pool and than deletes the software license.
2013 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2014 # @param softwareLicenseId Identificator of a license.
2015 # @param licensePoolId The name of the pool.
2016 sub opsi_removeLicense {
2017 my $startTime = Time::HiRes::time;
2018 my ($msg, $msg_hash, $session_id) = @_;
2019 my $header = @{$msg_hash->{'header'}}[0];
2020 my $source = @{$msg_hash->{'source'}}[0];
2022 # Check input sanity
2023 my $softwareLicenseId;
2024 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2025 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2026 } else {
2027 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2028 }
2029 my $licensePoolId;
2030 if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2031 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2032 } else {
2033 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2034 }
2036 # Call Opsi
2037 my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2038 if ($err){
2039 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2040 }
2042 # Call Opsi
2043 ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2044 if ($err){
2045 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2046 }
2048 # Create hash for the answer
2049 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2050 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2051 my $endTime = Time::HiRes::time;
2052 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2053 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2054 return ( &create_xml_string($out_hash) );
2055 }
2058 ################################
2059 # @brief Return softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseContractId, expirationDate, boundToHost and a list of productIds.
2060 # @param hostId Something like client_1.intranet.mydomain.de
2061 sub opsi_getReservedLicenses {
2062 my $startTime = Time::HiRes::time;
2063 my ($msg, $msg_hash, $session_id) = @_;
2064 my $header = @{$msg_hash->{'header'}}[0];
2065 my $source = @{$msg_hash->{'source'}}[0];
2067 # Check input sanity
2068 my $hostId;
2069 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2070 $hostId = @{$msg_hash->{'hostId'}}[0];
2071 } else {
2072 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2073 }
2075 # Fetch informations from Opsi server
2076 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2077 if ($license_err){
2078 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2079 }
2081 # Parse result
2082 my $res_hash = { 'hit'=> [] };
2083 foreach my $license ( @$license_res) {
2084 if ($license->{boundToHost} ne $hostId) { next; }
2086 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2087 'maxInstallations' => [$license->{'maxInstallations'}],
2088 'boundToHost' => [$license->{'boundToHost'}],
2089 'expirationDate' => [$license->{'expirationDate'}],
2090 'licenseContractId' => [$license->{'licenseContractId'}],
2091 'licenseType' => [$license->{'licenseType'}],
2092 'licensePoolIds' => [],
2093 };
2095 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2096 # Fetch information for license pools containing a software license which is bound to given host
2097 my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2098 if ($pool_err){
2099 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2100 }
2102 # Add licensePool information to result hash
2103 push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2104 $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2105 map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2106 map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2107 }
2108 push( @{$res_hash->{hit}}, $license_hash );
2109 }
2110 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2111 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2112 $out_hash->{licenses} = [$res_hash];
2114 my $endTime = Time::HiRes::time;
2115 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2116 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2117 return ( &create_xml_string($out_hash) );
2118 }
2120 ################################
2121 # @brief Bound the given softwareLicenseId to the given host.
2122 # @param hostId Opsi hostId
2123 # @param softwareLicenseId Identificator of a license (optional).
2124 sub opsi_boundHostToLicense {
2125 my $startTime = Time::HiRes::time;
2126 my ($msg, $msg_hash, $session_id) = @_;
2127 my $header = @{$msg_hash->{'header'}}[0];
2128 my $source = @{$msg_hash->{'source'}}[0];
2130 # Check input sanity
2131 my $hostId;
2132 if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2133 $hostId = @{$msg_hash->{'hostId'}}[0];
2134 } else {
2135 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2136 }
2137 my $softwareLicenseId;
2138 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2139 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2140 } else {
2141 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2142 }
2144 # Fetch informations from Opsi server
2145 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2146 if ($license_err){
2147 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2148 }
2150 # Memorize parameter for given softwareLicenseId
2151 my $licenseContractId;
2152 my $licenseType;
2153 my $maxInstallations;
2154 my $boundToHost;
2155 my $expirationDate = "";
2156 my $found;
2157 foreach my $license (@$license_res) {
2158 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2159 $licenseContractId = $license->{licenseContractId};
2160 $licenseType = $license->{licenseType};
2161 $maxInstallations = $license->{maxInstallations};
2162 $expirationDate = $license->{expirationDate};
2163 $found++;
2164 }
2166 if (not $found) {
2167 return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
2168 }
2170 # Set boundToHost option for a given software license
2171 my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId,
2172 'licenseContractId' => $licenseContractId,
2173 'licenseType' => $licenseType,
2174 'maxInstallations' => $maxInstallations,
2175 'boundToHost' => $hostId,
2176 'expirationDate' => $expirationDate);
2177 if ($bound_err) {
2178 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2179 }
2181 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2182 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2184 my $endTime = Time::HiRes::time;
2185 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2186 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
2187 return ( &create_xml_string($out_hash) );
2188 }
2190 ################################
2191 # @brief Release a software license formerly bound to a host.
2192 # @param softwareLicenseId Identificator of a license.
2193 sub opsi_unboundHostFromLicense {
2194 # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2195 # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2196 # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2197 my $startTime = Time::HiRes::time;
2198 my ($msg, $msg_hash, $session_id) = @_;
2199 my $header = @{$msg_hash->{'header'}}[0];
2200 my $source = @{$msg_hash->{'source'}}[0];
2202 # Check input sanity
2203 my $softwareLicenseId;
2204 if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2205 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2206 } else {
2207 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2208 }
2210 # Memorize parameter witch are required for this procedure
2211 my $licenseContractId;
2212 my $licenseType;
2213 my $maxInstallations;
2214 my $expirationDate;
2215 my $partner;
2216 my $conclusionDate;
2217 my $notificationDate;
2218 my $notes;
2219 my $licensePoolId;
2220 my $licenseKey;
2222 # Fetch license informations from Opsi server
2223 my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2224 if ($license_err){
2225 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2226 }
2227 my $found = 0;
2228 foreach my $license (@$license_res) {
2229 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2230 $licenseContractId = $license->{licenseContractId};
2231 $licenseType = $license->{licenseType};
2232 $maxInstallations = $license->{maxInstallations};
2233 $expirationDate = $license->{expirationDate};
2234 $licensePoolId = @{$license->{licensePoolIds}}[0];
2235 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2236 $found++;
2237 }
2239 # Fetch contract informations from Opsi server
2240 my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2241 if ($contract_err){
2242 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2243 }
2244 $partner = $contract_res->{partner};
2245 $conclusionDate = $contract_res->{conclusionDate};
2246 $notificationDate = $contract_res->{notificationDate};
2247 $expirationDate = $contract_res->{expirationDate};
2248 $notes = $contract_res->{notes};
2250 # Delete software license
2251 my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2252 if ($err) {
2253 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2254 }
2256 # Recreate software license without boundToHost
2257 ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate,
2258 'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2259 if ($err) {
2260 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2261 }
2262 ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType,
2263 'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate );
2264 if ($err) {
2265 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2266 }
2267 ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2268 if ($err) {
2269 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2270 }
2272 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2273 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2275 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2276 return ( &create_xml_string($out_hash) );
2277 }
2279 ################################
2280 # @brief Returns a list of licenses with softwaerLicenseId, maxInstallations, boundToHost, expirationDate, licenseContractId, licenseType, a list of licensePoolIds with associated licenseKeys
2281 sub opsi_getAllSoftwareLicenses {
2282 my $startTime = Time::HiRes::time;
2283 my ($msg, $msg_hash, $session_id) = @_;
2284 my $header = @{$msg_hash->{'header'}}[0];
2285 my $source = @{$msg_hash->{'source'}}[0];
2287 my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
2288 if ($err) {
2289 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
2290 }
2292 # Parse result
2293 my $res_hash = { 'hit'=> [] };
2294 foreach my $license ( @$res) {
2295 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2296 'maxInstallations' => [$license->{'maxInstallations'}],
2297 'boundToHost' => [$license->{'boundToHost'}],
2298 'expirationDate' => [$license->{'expirationDate'}],
2299 'licenseContractId' => [$license->{'licenseContractId'}],
2300 'licenseType' => [$license->{'licenseType'}],
2301 'licensePoolIds' => [],
2302 'licenseKeys'=> {}
2303 };
2304 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2305 push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2306 $license_hash->{licenseKeys}->{$licensePoolId} = [ $license->{'licenseKeys'}->{$licensePoolId} ];
2307 }
2308 push( @{$res_hash->{hit}}, $license_hash );
2309 }
2311 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2312 $out_hash->{licenses} = [$res_hash];
2313 if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
2315 &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
2316 return ( &create_xml_string($out_hash) );
2317 }
2319 sub opsi_test {
2320 my ($msg, $msg_hash, $session_id) = @_;
2321 my $header = @{$msg_hash->{'header'}}[0];
2322 my $source = @{$msg_hash->{'source'}}[0];
2323 my $pram1 = @{$msg_hash->{'productId'}}[0];
2325 print STDERR Dumper $pram1;
2327 # Fetch infos from Opsi server
2328 my $callobj = {
2329 method => 'getLicensePoolId',
2330 params => [ $pram1 ],
2331 id => 1,
2332 };
2333 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2335 print STDERR Dumper $res;
2336 return ();
2337 }
2340 # ----------------------------------------------------------------------------
2341 # internal methods handling the comunication with Opsi
2342 # ----------------------------------------------------------------------------
2344 ################################
2345 # @brief Checks if there is a specified tag and if the the tag has a content.
2346 sub _check_xml_tag_is_ok {
2347 my ($msg_hash,$tag) = @_;
2348 if (not defined $msg_hash->{$tag}) {
2349 $_ = "message contains no tag '$tag'";
2350 return 0;
2351 }
2352 if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
2353 $_ = "message tag '$tag' has no content";
2354 return 0;
2355 }
2356 return 1;
2357 }
2359 ################################
2360 # @brief Writes the log line and returns the error message for GOsa.
2361 sub _giveErrorFeedback {
2362 my ($msg_hash, $err_string, $session_id) = @_;
2363 &main::daemon_log("$session_id ERROR: $err_string", 1);
2364 my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2365 if (exists $msg_hash->{forward_to_gosa}) {
2366 &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
2367 }
2368 return ( &create_xml_string($out_hash) );
2369 }
2372 ################################
2373 # @brief Perform the call to the Opsi server and measure the time for the call
2374 sub _callOpsi {
2375 my %arg = ('method'=>undef, 'params'=>[], 'id'=>1, @_);
2377 my $callObject = {
2378 method => $arg{method},
2379 params => $arg{params},
2380 id => $arg{id},
2381 };
2383 my $startTime = Time::HiRes::time;
2384 my $opsiResult = $opsi_client->call($opsi_url, $callObject);
2385 my $endTime = Time::HiRes::time;
2386 my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
2388 &main::daemon_log("0 DEBUG: time to process opsi call '$arg{method}' : $elapsedTime seconds", 1034);
2390 return $opsiResult;
2391 }
2393 sub _getLicensePool_hash {
2394 my %arg = ( 'licensePoolId' => undef, @_ );
2396 if (not defined $arg{licensePoolId} ) {
2397 return ("function requires licensePoolId as parameter", 1);
2398 }
2400 my $res = &_callOpsi( method => 'getLicensePool_hash', params =>[$arg{licensePoolId}], id => 1 );
2401 my ($res_error, $res_error_str) = &check_opsi_res($res);
2402 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2404 return ($res->result, 0);
2405 }
2407 sub _getSoftwareLicenses_listOfHashes {
2409 my $res = &_callOpsi( method => 'getSoftwareLicenses_listOfHashes' );
2410 my ($res_error, $res_error_str) = &check_opsi_res($res);
2411 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2413 return ($res->result, 0);
2414 }
2416 sub _getSoftwareLicenseUsages_listOfHashes {
2417 my %arg = ( 'hostId' => "", 'licensePoolId' => "", @_ );
2419 my $res = &_callOpsi( method=>'getSoftwareLicenseUsages_listOfHashes', params=>[ $arg{hostId}, $arg{licensePoolId} ] );
2420 my ($res_error, $res_error_str) = &check_opsi_res($res);
2421 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2423 return ($res->result, 0);
2424 }
2426 sub _removeSoftwareLicenseFromLicensePool {
2427 my %arg = ( 'softwareLicenseId' => undef, 'licensePoolId' => undef, @_ );
2429 if (not defined $arg{softwareLicenseId} ) {
2430 return ("function requires softwareLicenseId as parameter", 1);
2431 }
2432 if (not defined $arg{licensePoolId} ) {
2433 return ("function requires licensePoolId as parameter", 1);
2434 }
2436 my $res = &_callOpsi( method=>'removeSoftwareLicenseFromLicensePool', params=>[ $arg{softwareLicenseId}, $arg{licensePoolId} ] );
2437 my ($res_error, $res_error_str) = &check_opsi_res($res);
2438 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2440 return ($res->result, 0);
2441 }
2443 sub _deleteSoftwareLicense {
2444 my %arg = ( 'softwareLicenseId' => undef, 'removeFromPools' => "false", @_ );
2446 if (not defined $arg{softwareLicenseId} ) {
2447 return ("function requires softwareLicenseId as parameter", 1);
2448 }
2449 my $removeFromPools = "";
2450 if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) {
2451 $removeFromPools = "removeFromPools";
2452 }
2454 my $res = &_callOpsi( method=>'deleteSoftwareLicense', params=>[ $arg{softwareLicenseId}, $removeFromPools ] );
2455 my ($res_error, $res_error_str) = &check_opsi_res($res);
2456 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2458 return ($res->result, 0);
2459 }
2461 sub _getLicensePoolId {
2462 my %arg = ( 'productId' => undef, @_ );
2464 if (not defined $arg{productId} ) {
2465 return ("function requires productId as parameter", 1);
2466 }
2468 my $res = &_callOpsi( method => 'getLicensePoolId', params => [ $arg{productId} ] );
2469 my ($res_error, $res_error_str) = &check_opsi_res($res);
2470 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2472 return ($res->result, 0);
2473 }
2475 sub _getLicenseContract_hash {
2476 my %arg = ( 'licenseContractId' => undef, @_ );
2478 if (not defined $arg{licenseContractId} ) {
2479 return ("function requires licenseContractId as parameter", 1);
2480 }
2482 my $res = &_callOpsi( method => 'getLicenseContract_hash', params => [ $arg{licenseContractId} ] );
2483 my ($res_error, $res_error_str) = &check_opsi_res($res);
2484 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2486 return ($res->result, 0);
2487 }
2489 sub _createLicenseContract {
2490 my %arg = (
2491 'licenseContractId' => undef,
2492 'partner' => undef,
2493 'conclusionDate' => undef,
2494 'notificationDate' => undef,
2495 'expirationDate' => undef,
2496 'notes' => undef,
2497 @_ );
2499 my $res = &_callOpsi( method => 'createLicenseContract',
2500 params => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2501 );
2502 my ($res_error, $res_error_str) = &check_opsi_res($res);
2503 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2505 return ($res->result, 0);
2506 }
2508 sub _createSoftwareLicense {
2509 my %arg = (
2510 'softwareLicenseId' => undef,
2511 'licenseContractId' => undef,
2512 'licenseType' => undef,
2513 'maxInstallations' => undef,
2514 'boundToHost' => undef,
2515 'expirationDate' => undef,
2516 @_ );
2518 my $res = &_callOpsi( method => 'createSoftwareLicense',
2519 params => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2520 );
2521 my ($res_error, $res_error_str) = &check_opsi_res($res);
2522 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2524 return ($res->result, 0);
2525 }
2527 sub _addSoftwareLicenseToLicensePool {
2528 my %arg = (
2529 'softwareLicenseId' => undef,
2530 'licensePoolId' => undef,
2531 'licenseKey' => undef,
2532 @_ );
2534 if (not defined $arg{softwareLicenseId} ) {
2535 return ("function requires softwareLicenseId as parameter", 1);
2536 }
2537 if (not defined $arg{licensePoolId} ) {
2538 return ("function requires licensePoolId as parameter", 1);
2539 }
2541 my $res = &_callOpsi( method => 'addSoftwareLicenseToLicensePool', params => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ] );
2542 my ($res_error, $res_error_str) = &check_opsi_res($res);
2543 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2545 return ($res->result, 0);
2546 }
2548 sub _getProductStates_hash {
2549 my %arg = ( 'hostId' => undef, @_ );
2551 if (not defined $arg{hostId} ) {
2552 return ("function requires hostId as parameter", 1);
2553 }
2555 my $res = &_callOpsi( method => 'getProductStates_hash', params => [$arg{hostId}]);
2556 my ($res_error, $res_error_str) = &check_opsi_res($res);
2557 if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2559 return ($res->result, 0);
2560 }
2562 1;