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 );
25 @EXPORT = @events;
27 use strict;
28 use warnings;
29 use GOSA::GosaSupportDaemon;
30 use Data::Dumper;
31 use XML::Quote qw(:all);
34 BEGIN {}
36 END {}
38 ## @method get_events()
39 # A brief function returning a list of functions which are exported by importing the module.
40 # @return List of all provided functions
41 sub get_events {
42 return \@events;
43 }
45 ## @method opsi_add_product_to_client
46 # Adds an Opsi product to an Opsi client.
47 # @param msg - STRING - xml message with tags hostId and productId
48 # @param msg_hash - HASHREF - message information parsed into a hash
49 # @param session_id - INTEGER - POE session id of the processing of this message
50 # @return out_msg - STRING - feedback to GOsa in success and error case
51 sub opsi_add_product_to_client {
52 my ($msg, $msg_hash, $session_id) = @_;
53 my $header = @{$msg_hash->{'header'}}[0];
54 my $source = @{$msg_hash->{'source'}}[0];
55 my $target = @{$msg_hash->{'target'}}[0];
56 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
57 my ($hostId, $productId);
58 my $error = 0;
60 # Build return message
61 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
62 if (defined $forward_to_gosa) {
63 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
64 }
66 # Sanity check of needed parameter
67 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1)) {
68 $error++;
69 &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
70 &add_content2xml_hash($out_hash, "error", "hostId");
71 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
73 }
74 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1)) {
75 $error++;
76 &add_content2xml_hash($out_hash, "productId_error", "no productId specified or productId tag invalid");
77 &add_content2xml_hash($out_hash, "error", "productId");
78 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
79 }
81 if (not $error) {
82 # Get hostID
83 $hostId = @{$msg_hash->{'hostId'}}[0];
84 &add_content2xml_hash($out_hash, "hostId", $hostId);
86 # Get productID
87 $productId = @{$msg_hash->{'productId'}}[0];
88 &add_content2xml_hash($out_hash, "productId", $productId);
90 # Do an action request for all these -> "setup".
91 my $callobj = {
92 method => 'setProductActionRequest',
93 params => [ $productId, $hostId, "setup" ],
94 id => 1, };
96 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
97 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
98 if ($sres_err){
99 &main::daemon_log("$session_id ERROR: cannot add product: ".$sres_err_string, 1);
100 &add_content2xml_hash($out_hash, "error", $sres_err_string);
101 }
102 }
104 # return message
105 return ( &create_xml_string($out_hash) );
106 }
108 ## @method opsi_del_product_from_client
109 # Deletes an Opsi-product from an Opsi-client.
110 # @param msg - STRING - xml message with tags hostId and productId
111 # @param msg_hash - HASHREF - message information parsed into a hash
112 # @param session_id - INTEGER - POE session id of the processing of this message
113 # @return out_msg - STRING - feedback to GOsa in success and error case
114 sub opsi_del_product_from_client {
115 my ($msg, $msg_hash, $session_id) = @_;
116 my $header = @{$msg_hash->{'header'}}[0];
117 my $source = @{$msg_hash->{'source'}}[0];
118 my $target = @{$msg_hash->{'target'}}[0];
119 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
120 my ($hostId, $productId);
121 my $error = 0;
122 my ($sres, $sres_err, $sres_err_string);
124 # Build return message
125 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
126 if (defined $forward_to_gosa) {
127 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
128 }
130 # Sanity check of needed parameter
131 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1)) {
132 $error++;
133 &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
134 &add_content2xml_hash($out_hash, "error", "hostId");
135 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
137 }
138 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1)) {
139 $error++;
140 &add_content2xml_hash($out_hash, "productId_error", "no productId specified or productId tag invalid");
141 &add_content2xml_hash($out_hash, "error", "productId");
142 &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1);
143 }
145 # All parameter available
146 if (not $error) {
147 # Get hostID
148 $hostId = @{$msg_hash->{'hostId'}}[0];
149 &add_content2xml_hash($out_hash, "hostId", $hostId);
151 # Get productID
152 $productId = @{$msg_hash->{'productId'}}[0];
153 &add_content2xml_hash($out_hash, "productId", $productId);
156 #TODO: check the results for more than one entry which is currently installed
157 #$callobj = {
158 # method => 'getProductDependencies_listOfHashes',
159 # params => [ $productId ],
160 # id => 1, };
161 #
162 #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
163 #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
164 #if ($sres_err){
165 # &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
166 # &add_content2xml_hash($out_hash, "error", $sres_err_string);
167 # return ( &create_xml_string($out_hash) );
168 #}
171 # Check to get product action list
172 my $callobj = {
173 method => 'getPossibleProductActions_list',
174 params => [ $productId ],
175 id => 1, };
176 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
177 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
178 if ($sres_err){
179 &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
180 &add_content2xml_hash($out_hash, "error", $sres_err_string);
181 $error++;
182 }
183 }
185 # Check action uninstall of product
186 if (not $error) {
187 my $uninst_possible= 0;
188 foreach my $r (@{$sres->result}) {
189 if ($r eq 'uninstall') {
190 $uninst_possible= 1;
191 }
192 }
193 if (!$uninst_possible){
194 &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
195 &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
196 $error++;
197 }
198 }
200 # Set product state to "none"
201 # Do an action request for all these -> "setup".
202 if (not $error) {
203 my $callobj = {
204 method => 'setProductActionRequest',
205 params => [ $productId, $hostId, "none" ],
206 id => 1,
207 };
208 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
209 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
210 if ($sres_err){
211 &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
212 &add_content2xml_hash($out_hash, "error", $sres_err_string);
213 }
214 }
216 # Return message
217 return ( &create_xml_string($out_hash) );
218 }
220 ## @method opsi_add_client
221 # Adds an Opsi client to Opsi.
222 # @param msg - STRING - xml message with tags hostId and macaddress
223 # @param msg_hash - HASHREF - message information parsed into a hash
224 # @param session_id - INTEGER - POE session id of the processing of this message
225 # @return out_msg - STRING - feedback to GOsa in success and error case
226 sub opsi_add_client {
227 my ($msg, $msg_hash, $session_id) = @_;
228 my $header = @{$msg_hash->{'header'}}[0];
229 my $source = @{$msg_hash->{'source'}}[0];
230 my $target = @{$msg_hash->{'target'}}[0];
231 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
232 my ($hostId, $mac);
233 my $error = 0;
234 my ($sres, $sres_err, $sres_err_string);
236 # build return message with twisted target and source
237 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
238 if (defined $forward_to_gosa) {
239 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
240 }
242 # Sanity check of needed parameter
243 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1)) {
244 $error++;
245 &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
246 &add_content2xml_hash($out_hash, "error", "hostId");
247 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
248 }
249 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1)) {
250 $error++;
251 &add_content2xml_hash($out_hash, "macaddress_error", "no macaddress specified or macaddress tag invalid");
252 &add_content2xml_hash($out_hash, "error", "macaddress");
253 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
254 }
256 if (not $error) {
257 # Get hostID
258 $hostId = @{$msg_hash->{'hostId'}}[0];
259 &add_content2xml_hash($out_hash, "hostId", $hostId);
261 # Get macaddress
262 $mac = @{$msg_hash->{'macaddress'}}[0];
263 &add_content2xml_hash($out_hash, "macaddress", $mac);
265 my $name= $hostId;
266 $name=~ s/^([^.]+).*$/$1/;
267 my $domain= $hostId;
268 $domain=~ s/^[^.]+\.(.*)$/$1/;
269 my ($description, $notes, $ip);
271 if (defined @{$msg_hash->{'description'}}[0]){
272 $description = @{$msg_hash->{'description'}}[0];
273 }
274 if (defined @{$msg_hash->{'notes'}}[0]){
275 $notes = @{$msg_hash->{'notes'}}[0];
276 }
277 if (defined @{$msg_hash->{'ip'}}[0]){
278 $ip = @{$msg_hash->{'ip'}}[0];
279 }
281 my $callobj;
282 $callobj = {
283 method => 'createClient',
284 params => [ $name, $domain, $description, $notes, $ip, $mac ],
285 id => 1,
286 };
288 $sres = $main::opsi_client->call($main::opsi_url, $callobj);
289 ($sres_err, $sres_err_string) = &check_opsi_res($sres);
290 if ($sres_err){
291 &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
292 &add_content2xml_hash($out_hash, "error", $sres_err_string);
293 } else {
294 &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5);
295 }
296 }
298 # Return message
299 return ( &create_xml_string($out_hash) );
300 }
302 ## @method opsi_modify_client
303 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
304 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
305 # @param msg_hash - HASHREF - message information parsed into a hash
306 # @param session_id - INTEGER - POE session id of the processing of this message
307 # @return out_msg - STRING - feedback to GOsa in success and error case
308 sub opsi_modify_client {
309 my ($msg, $msg_hash, $session_id) = @_;
310 my $header = @{$msg_hash->{'header'}}[0];
311 my $source = @{$msg_hash->{'source'}}[0];
312 my $target = @{$msg_hash->{'target'}}[0];
313 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
314 my $hostId;
315 my $error = 0;
316 my ($sres, $sres_err, $sres_err_string);
318 # Build return message with twisted target and source
319 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
320 if (defined $forward_to_gosa) {
321 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
322 }
324 # Sanity check of needed parameter
325 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1)) {
326 $error++;
327 &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
328 &add_content2xml_hash($out_hash, "error", "hostId");
329 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
330 }
332 if (not $error) {
333 # Get hostID
334 $hostId = @{$msg_hash->{'hostId'}}[0];
335 &add_content2xml_hash($out_hash, "hostId", $hostId);
336 my $name= $hostId;
337 $name=~ s/^([^.]+).*$/$1/;
338 my $domain= $hostId;
339 $domain=~ s/^[^.]+(.*)$/$1/;
341 # Modify description, notes or mac if defined
342 my ($description, $notes, $mac);
343 my $callobj;
344 if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
345 $description = @{$msg_hash->{'description'}}[0];
346 $callobj = {
347 method => 'setHostDescription',
348 params => [ $hostId, $description ],
349 id => 1,
350 };
351 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
352 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
353 if ($sres_err){
354 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
355 &add_content2xml_hash($out_hash, "error", $sres_err_string);
356 }
357 }
358 if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
359 $notes = @{$msg_hash->{'notes'}}[0];
360 $callobj = {
361 method => 'setHostNotes',
362 params => [ $hostId, $notes ],
363 id => 1,
364 };
365 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
366 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
367 if ($sres_err){
368 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
369 &add_content2xml_hash($out_hash, "error", $sres_err_string);
370 }
371 }
372 if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
373 $mac = @{$msg_hash->{'mac'}}[0];
374 $callobj = {
375 method => 'setMacAddress',
376 params => [ $hostId, $mac ],
377 id => 1,
378 };
379 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
380 my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
381 if ($sres_err){
382 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
383 &add_content2xml_hash($out_hash, "error", $sres_err_string);
384 }
385 }
386 }
388 # Return message
389 return ( &create_xml_string($out_hash) );
390 }
393 ## @method opsi_get_netboot_products
394 # Get netboot products for specific host.
395 # @param msg - STRING - xml message with tag hostId
396 # @param msg_hash - HASHREF - message information parsed into a hash
397 # @param session_id - INTEGER - POE session id of the processing of this message
398 # @return out_msg - STRING - feedback to GOsa in success and error case
399 sub opsi_get_netboot_products {
400 my ($msg, $msg_hash, $session_id) = @_;
401 my $header = @{$msg_hash->{'header'}}[0];
402 my $source = @{$msg_hash->{'source'}}[0];
403 my $target = @{$msg_hash->{'target'}}[0];
404 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
405 my $hostId;
406 my $error = 0;
407 my $xml_msg;
409 # Build return message with twisted target and source
410 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
411 if (defined $forward_to_gosa) {
412 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
413 }
415 # Sanity check of needed parameter
416 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1)) {
417 $error++;
418 &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
419 &add_content2xml_hash($out_hash, "error", "hostId");
420 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
421 }
423 if (not $error) {
425 # Get hostID if defined
426 $hostId = @{$msg_hash->{'hostId'}}[0];
427 &add_content2xml_hash($out_hash, "hostId", $hostId);
429 &add_content2xml_hash($out_hash, "xxx", "");
430 $xml_msg= &create_xml_string($out_hash);
432 # For hosts, only return the products that are or get installed
433 my $callobj;
434 $callobj = {
435 method => 'getNetBootProductIds_list',
436 params => [ ],
437 id => 1,
438 };
440 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
441 my %r = ();
442 for (@{$res->result}) { $r{$_} = 1 }
444 if (not &check_opsi_res($res)){
446 if (defined $hostId){
447 $callobj = {
448 method => 'getProductStates_hash',
449 params => [ $hostId ],
450 id => 1,
451 };
453 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
454 if (not &check_opsi_res($hres)){
455 my $htmp= $hres->result->{$hostId};
457 # check state != not_installed or action == setup -> load and add
458 foreach my $product (@{$htmp}){
460 if (!defined ($r{$product->{'productId'}})){
461 next;
462 }
464 # Now we've a couple of hashes...
465 if ($product->{'installationStatus'} ne "not_installed" or
466 $product->{'actionRequest'} eq "setup"){
467 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
469 $callobj = {
470 method => 'getProduct_hash',
471 params => [ $product->{'productId'} ],
472 id => 1,
473 };
475 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
476 if (not &check_opsi_res($sres)){
477 my $tres= $sres->result;
479 my $name= xml_quote($tres->{'name'});
480 my $r= $product->{'productId'};
481 my $description= xml_quote($tres->{'description'});
482 $name=~ s/\//\\\//;
483 $description=~ s/\//\\\//;
484 $xml_msg=~ s/<xxx><\/xxx>/<item><productId>$r<\/productId><name><\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
485 }
486 }
487 }
489 }
491 } else {
492 foreach my $r (@{$res->result}) {
493 $callobj = {
494 method => 'getProduct_hash',
495 params => [ $r ],
496 id => 1,
497 };
499 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
500 if (not &check_opsi_res($sres)){
501 my $tres= $sres->result;
503 my $name= xml_quote($tres->{'name'});
504 my $description= xml_quote($tres->{'description'});
505 $name=~ s/\//\\\//;
506 $description=~ s/\//\\\//;
507 $xml_msg=~ s/<xxx><\/xxx>/<item><productId>$r<\/productId><name><\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
508 }
509 }
511 }
512 }
513 $xml_msg=~ s/<xxx><\/xxx>//;
514 }
516 # Return message
517 return ( $xml_msg );
518 }
521 ## @method opsi_get_product_properties
522 # Get product properties for a product and a specific host or gobally for a product.
523 # @param msg - STRING - xml message with tags productId and optional hostId
524 # @param msg_hash - HASHREF - message information parsed into a hash
525 # @param session_id - INTEGER - POE session id of the processing of this message
526 # @return out_msg - STRING - feedback to GOsa in success and error case
527 sub opsi_get_product_properties {
528 my ($msg, $msg_hash, $session_id) = @_;
529 my $header = @{$msg_hash->{'header'}}[0];
530 my $source = @{$msg_hash->{'source'}}[0];
531 my $target = @{$msg_hash->{'target'}}[0];
532 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
533 my ($hostId, $productId);
534 my $error = 0;
535 my $xml_msg;
537 # build return message with twisted target and source
538 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
539 if (defined $forward_to_gosa) {
540 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
541 }
543 # Sanity check of needed parameter
544 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1)) {
545 $error++;
546 &add_content2xml_hash($out_hash, "productId_error", "no productId specified or productId tag invalid");
547 &add_content2xml_hash($out_hash, "error", "productId");
548 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
549 }
551 if (not $error) {
553 # Get productid
554 $productId = @{$msg_hash->{'productId'}}[0];
555 &add_content2xml_hash($out_hash, "producId", "$productId");
558 $hostId = @{$msg_hash->{'hostId'}}[0];
559 &add_content2xml_hash($out_hash, "hostId", $hostId);
561 # Load actions
562 my $callobj = {
563 method => 'getPossibleProductActions_list',
564 params => [ $productId ],
565 id => 1,
566 };
567 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
568 if (not &check_opsi_res($res)){
569 foreach my $action (@{$res->result}){
570 &add_content2xml_hash($out_hash, "action", $action);
571 }
572 }
574 # Add place holder
575 &add_content2xml_hash($out_hash, "xxx", "");
577 }
579 # Move to XML string
580 $xml_msg= &create_xml_string($out_hash);
582 if (not $error) {
584 # JSON Query
585 my $callobj = {
586 method => 'getProductProperties_hash',
587 params => [ $productId ],
588 id => 1,
589 };
590 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
591 if (not &check_opsi_res($res)){
592 my $r= $res->result;
593 foreach my $key (keys %{$r}) {
594 my $item= "<item>";
595 my $value= $r->{$key};
596 if (UNIVERSAL::isa( $value, "ARRAY" )){
597 foreach my $subval (@{$value}){
598 $item.= "<$key>".xml_quote($subval)."</$key>";
599 }
600 } else {
601 $item.= "<$key>".xml_quote($value)."</$key>";
602 }
603 $item.= "</item>";
604 $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
605 }
606 }
608 $xml_msg=~ s/<xxx><\/xxx>//;
609 }
611 # Return message
612 return ( $xml_msg );
613 }
616 ## @method opsi_set_product_properties
617 # 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.
618 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
619 # @param msg_hash - HASHREF - message information parsed into a hash
620 # @param session_id - INTEGER - POE session id of the processing of this message
621 # @return out_msg - STRING - feedback to GOsa in success and error case
622 sub opsi_set_product_properties {
623 my ($msg, $msg_hash, $session_id) = @_;
624 my $header = @{$msg_hash->{'header'}}[0];
625 my $source = @{$msg_hash->{'source'}}[0];
626 my $target = @{$msg_hash->{'target'}}[0];
627 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
628 my ($productId, $hostId);
629 my $error = 0;
630 print STDERR Dumper($msg_hash);
631 # Build return message with twisted target and source
632 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
633 if (defined $forward_to_gosa) {
634 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
635 }
637 # Sanity check of needed parameter
638 if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1)) {
639 $error++;
640 &add_content2xml_hash($out_hash, "productId_error", "no productId specified or productId tag invalid");
641 &add_content2xml_hash($out_hash, "error", "productId");
642 &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1);
643 }
644 if (not exists $msg_hash->{'item'}) {
645 $error++;
646 &add_content2xml_hash($out_hash, "item_error", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
647 &add_content2xml_hash($out_hash, "error", "item");
648 &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1);
649 } else {
650 if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
651 $error++;
652 &add_content2xml_hash($out_hash, "name_error", "message needs within the xml-tag 'item' one xml-tags 'name'");
653 &add_content2xml_hash($out_hash, "error", "name");
654 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1);
655 }
656 if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
657 $error++;
658 &add_content2xml_hash($out_hash, "value_error", "message needs within the xml-tag 'item' one xml-tags 'value'");
659 &add_content2xml_hash($out_hash, "error", "value");
660 &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1);
661 }
662 }
663 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1)) {
664 $error++;
665 &add_content2xml_hash($out_hash, "hostId_error", "hostId contains no or more than one values");
666 &add_content2xml_hash($out_hash, "error", "hostId");
667 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
668 }
670 if (not $error) {
672 # Get productId
673 $productId = @{$msg_hash->{'productId'}}[0];
674 &add_content2xml_hash($out_hash, "productId", $productId);
676 # Get hostID if defined
677 if (exists $msg_hash->{'hostId'}){
678 $hostId = @{$msg_hash->{'hostId'}}[0];
679 &add_content2xml_hash($out_hash, "hostId", $hostId);
680 }
682 # Set product states if requested
683 if (defined @{$msg_hash->{'action'}}[0]){
684 &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
685 }
686 if (defined @{$msg_hash->{'state'}}[0]){
687 &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
688 }
690 # Find properties
691 foreach my $item (@{$msg_hash->{'item'}}){
692 # JSON Query
693 my $callobj;
695 if (defined $hostId){
696 $callobj = {
697 method => 'setProductProperty',
698 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
699 id => 1,
700 };
701 } else {
702 $callobj = {
703 method => 'setProductProperty',
704 params => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
705 id => 1,
706 };
707 }
709 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
710 my ($res_err, $res_err_string) = &check_opsi_res($res);
711 # TODO : Diese Errormessage klingt komisch!
712 if (!$res_err){
713 &main::daemon_log("$session_id ERROR: no communication failed while setting '".$item->{'name'}[0]."'", 1);
714 &add_content2xml_hash($out_hash, "error", $res_err_string);
715 }
716 }
718 }
720 # Return message
721 return ( &create_xml_string($out_hash) );
722 }
725 ## @method opsi_get_client_hardware
726 # Reports client hardware inventory.
727 # @param msg - STRING - xml message with tag hostId
728 # @param msg_hash - HASHREF - message information parsed into a hash
729 # @param session_id - INTEGER - POE session id of the processing of this message
730 # @return out_msg - STRING - feedback to GOsa in success and error case
731 sub opsi_get_client_hardware {
732 my ($msg, $msg_hash, $session_id) = @_;
733 my $header = @{$msg_hash->{'header'}}[0];
734 my $source = @{$msg_hash->{'source'}}[0];
735 my $target = @{$msg_hash->{'target'}}[0];
736 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
737 my $hostId;
738 my $error = 0;
739 my $xml_msg;
741 # build return message with twisted target and source
742 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
743 if (defined $forward_to_gosa) {
744 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
745 }
747 # Sanity check of needed parameter
748 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1)) {
749 $error++;
750 &add_content2xml_hash($out_hash, "hostId_error", "hostId contains no or more than one values");
751 &add_content2xml_hash($out_hash, "error", "hostId");
752 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
753 }
755 if (not $error) {
757 # Get hostID
758 $hostId = @{$msg_hash->{'hostId'}}[0];
759 &add_content2xml_hash($out_hash, "hostId", "$hostId");
760 &add_content2xml_hash($out_hash, "xxx", "");
761 }
763 # Move to XML string
764 $xml_msg= &create_xml_string($out_hash);
766 if (not $error) {
768 # JSON Query
769 my $callobj = {
770 method => 'getHardwareInformation_hash',
771 params => [ $hostId ],
772 id => 1,
773 };
775 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
776 if (not &check_opsi_res($res)){
777 my $result= $res->result;
778 foreach my $r (keys %{$result}){
779 my $item= "<item><id>".xml_quote($r)."</id>";
780 my $value= $result->{$r};
781 foreach my $sres (@{$value}){
783 foreach my $dres (keys %{$sres}){
784 if (defined $sres->{$dres}){
785 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
786 }
787 }
789 }
790 $item.= "</item>";
791 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
793 }
794 }
796 $xml_msg=~ s/<xxx><\/xxx>//;
798 }
800 # Return message
801 return ( $xml_msg );
802 }
805 ## @method opsi_list_clients
806 # Reports all Opsi clients.
807 # @param msg - STRING - xml message
808 # @param msg_hash - HASHREF - message information parsed into a hash
809 # @param session_id - INTEGER - POE session id of the processing of this message
810 # @return out_msg - STRING - feedback to GOsa in success and error case
811 sub opsi_list_clients {
812 my ($msg, $msg_hash, $session_id) = @_;
813 my $header = @{$msg_hash->{'header'}}[0];
814 my $source = @{$msg_hash->{'source'}}[0];
815 my $target = @{$msg_hash->{'target'}}[0];
816 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
817 my $error = 0;
819 # build return message with twisted target and source
820 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
821 if (defined $forward_to_gosa) {
822 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
823 }
824 &add_content2xml_hash($out_hash, "xxx", "");
826 # Move to XML string
827 my $xml_msg= &create_xml_string($out_hash);
829 if (not $error) {
831 # JSON Query
832 my $callobj = {
833 method => 'getClients_listOfHashes',
834 params => [ ],
835 id => 1,
836 };
837 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
838 if (not &check_opsi_res($res)){
839 foreach my $host (@{$res->result}){
841 print STDERR Dumper($host);
842 my $item= "<item><name>".$host->{'hostId'}."</name>";
843 if (defined($host->{'description'})){
844 $item.= "<description>".xml_quote($host->{'description'})."</description>";
845 }
846 if (defined($host->{'ip'})){
847 $item.= "<ip>".xml_quote($host->{'ip'})."</ip>";
848 }
849 if (defined($host->{'mac'})){
850 $item.= "<mac>".xml_quote($host->{'mac'})."</mac>";
851 }
852 if (defined($host->{'notes'})){
853 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
854 }
855 $item.= "</item>";
856 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
857 }
858 }
859 }
861 $xml_msg=~ s/<xxx><\/xxx>//;
862 return ( $xml_msg );
863 }
866 ## @method opsi_get_client_software
867 # Reports client software inventory.
868 # @param msg - STRING - xml message with tag hostId
869 # @param msg_hash - HASHREF - message information parsed into a hash
870 # @param session_id - INTEGER - POE session id of the processing of this message
871 # @return out_msg - STRING - feedback to GOsa in success and error case
872 sub opsi_get_client_software {
873 my ($msg, $msg_hash, $session_id) = @_;
874 my $header = @{$msg_hash->{'header'}}[0];
875 my $source = @{$msg_hash->{'source'}}[0];
876 my $target = @{$msg_hash->{'target'}}[0];
877 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
878 my $error = 0;
879 my $hostId;
880 my $xml_msg;
882 # build return message with twisted target and source
883 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
884 if (defined $forward_to_gosa) {
885 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
886 }
888 # Sanity check of needed parameter
889 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1)) {
890 $error++;
891 &add_content2xml_hash($out_hash, "hostId_error", "hostId contains no or more than one values");
892 &add_content2xml_hash($out_hash, "error", "hostId");
893 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
894 }
896 if (not $error) {
898 # Get hostID
899 $hostId = @{$msg_hash->{'hostId'}}[0];
900 &add_content2xml_hash($out_hash, "hostId", "$hostId");
901 &add_content2xml_hash($out_hash, "xxx", "");
902 }
904 $xml_msg= &create_xml_string($out_hash);
906 if (not $error) {
908 # JSON Query
909 my $callobj = {
910 method => 'getSoftwareInformation_hash',
911 params => [ $hostId ],
912 id => 1,
913 };
915 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
916 if (not &check_opsi_res($res)){
917 my $result= $res->result;
918 # TODO : Ist das hier schon fertig???
919 }
921 $xml_msg=~ s/<xxx><\/xxx>//;
923 }
925 # Return message
926 return ( $xml_msg );
927 }
930 ## @method opsi_get_local_products
931 # Reports product for given hostId or globally.
932 # @param msg - STRING - xml message with optional tag hostId
933 # @param msg_hash - HASHREF - message information parsed into a hash
934 # @param session_id - INTEGER - POE session id of the processing of this message
935 # @return out_msg - STRING - feedback to GOsa in success and error case
936 sub opsi_get_local_products {
937 my ($msg, $msg_hash, $session_id) = @_;
938 my $header = @{$msg_hash->{'header'}}[0];
939 my $source = @{$msg_hash->{'source'}}[0];
940 my $target = @{$msg_hash->{'target'}}[0];
941 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
942 my $hostId;
943 my $error = 0;
945 # Build return message with twisted target and source
946 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
947 if (defined $forward_to_gosa) {
948 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
949 }
950 &add_content2xml_hash($out_hash, "xxx", "");
952 # Get hostID if defined
953 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1)) {
954 $hostId = @{$msg_hash->{'hostId'}}[0];
955 &add_content2xml_hash($out_hash, "hostId", $hostId);
956 }
958 # Move to XML string
959 my $xml_msg= &create_xml_string($out_hash);
961 # For hosts, only return the products that are or get installed
962 my $callobj;
963 $callobj = {
964 method => 'getLocalBootProductIds_list',
965 params => [ ],
966 id => 1,
967 };
969 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
970 my %r = ();
971 for (@{$res->result}) { $r{$_} = 1 }
973 if (not &check_opsi_res($res)){
975 if (defined $hostId){
976 $callobj = {
977 method => 'getProductStates_hash',
978 params => [ $hostId ],
979 id => 1,
980 };
982 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
983 if (not &check_opsi_res($hres)){
984 my $htmp= $hres->result->{$hostId};
986 # Check state != not_installed or action == setup -> load and add
987 foreach my $product (@{$htmp}){
989 if (!defined ($r{$product->{'productId'}})){
990 next;
991 }
993 # Now we've a couple of hashes...
994 if ($product->{'installationStatus'} ne "not_installed" or
995 $product->{'actionRequest'} eq "setup"){
996 my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
998 $callobj = {
999 method => 'getProduct_hash',
1000 params => [ $product->{'productId'} ],
1001 id => 1,
1002 };
1004 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1005 if (not &check_opsi_res($sres)){
1006 my $tres= $sres->result;
1008 my $name= xml_quote($tres->{'name'});
1009 my $r= $product->{'productId'};
1010 my $description= xml_quote($tres->{'description'});
1011 $name=~ s/\//\\\//;
1012 $description=~ s/\//\\\//;
1013 $xml_msg=~ s/<xxx><\/xxx>/<item><productId>$r<\/productId><name><\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1014 }
1016 }
1017 }
1019 }
1021 } else {
1022 foreach my $r (@{$res->result}) {
1023 $callobj = {
1024 method => 'getProduct_hash',
1025 params => [ $r ],
1026 id => 1,
1027 };
1029 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1030 if (not &check_opsi_res($sres)){
1031 my $tres= $sres->result;
1033 my $name= xml_quote($tres->{'name'});
1034 my $description= xml_quote($tres->{'description'});
1035 $name=~ s/\//\\\//;
1036 $description=~ s/\//\\\//;
1037 $xml_msg=~ s/<xxx><\/xxx>/<item><productId>$r<\/productId><name><\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1038 }
1040 }
1042 }
1043 }
1045 $xml_msg=~ s/<xxx><\/xxx>//;
1047 # Retrun Message
1048 return ( $xml_msg );
1049 }
1052 ## @method opsi_del_client
1053 # Deletes a client from Opsi.
1054 # @param msg - STRING - xml message with tag hostId
1055 # @param msg_hash - HASHREF - message information parsed into a hash
1056 # @param session_id - INTEGER - POE session id of the processing of this message
1057 # @return out_msg - STRING - feedback to GOsa in success and error case
1058 sub opsi_del_client {
1059 my ($msg, $msg_hash, $session_id) = @_;
1060 my $header = @{$msg_hash->{'header'}}[0];
1061 my $source = @{$msg_hash->{'source'}}[0];
1062 my $target = @{$msg_hash->{'target'}}[0];
1063 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1064 my $hostId;
1065 my $error = 0;
1067 # Build return message with twisted target and source
1068 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1069 if (defined $forward_to_gosa) {
1070 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1071 }
1073 # Sanity check of needed parameter
1074 if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1)) {
1075 $error++;
1076 &add_content2xml_hash($out_hash, "hostId_error", "hostId contains no or more than one values");
1077 &add_content2xml_hash($out_hash, "error", "hostId");
1078 &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1);
1079 }
1081 if (not $error) {
1083 # Get hostID
1084 $hostId = @{$msg_hash->{'hostId'}}[0];
1085 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1087 # JSON Query
1088 my $callobj = {
1089 method => 'deleteClient',
1090 params => [ $hostId ],
1091 id => 1,
1092 };
1093 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1094 }
1096 # Move to XML string
1097 my $xml_msg= &create_xml_string($out_hash);
1099 # Return message
1100 return ( $xml_msg );
1101 }
1104 ## @method opsi_install_client
1105 # Set a client in Opsi to install and trigger a wake on lan message (WOL).
1106 # @param msg - STRING - xml message with tags hostId, macaddress
1107 # @param msg_hash - HASHREF - message information parsed into a hash
1108 # @param session_id - INTEGER - POE session id of the processing of this message
1109 # @return out_msg - STRING - feedback to GOsa in success and error case
1110 sub opsi_install_client {
1111 my ($msg, $msg_hash, $session_id) = @_;
1112 my $header = @{$msg_hash->{'header'}}[0];
1113 my $source = @{$msg_hash->{'source'}}[0];
1114 my $target = @{$msg_hash->{'target'}}[0];
1115 my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1118 my ($hostId, $macaddress);
1120 my $error = 0;
1121 my @out_msg_l;
1123 # Build return message with twisted target and source
1124 my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1125 if (defined $forward_to_gosa) {
1126 &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1127 }
1129 # Sanity check of needed parameter
1130 if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1)) {
1131 $error++;
1132 &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
1133 &add_content2xml_hash($out_hash, "error", "hostId");
1134 &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1);
1135 }
1136 if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1)) {
1137 $error++;
1138 &add_content2xml_hash($out_hash, "macaddress_error", "no macaddress specified or macaddress tag invalid");
1139 &add_content2xml_hash($out_hash, "error", "macaddress");
1140 &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1);
1141 } else {
1142 if ((exists $msg_hash->{'macaddress'}) &&
1143 ($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)) {
1144 $macaddress = $1;
1145 } else {
1146 $error ++;
1147 &add_content2xml_hash($out_hash, "macaddress_error", "given mac address is not correct");
1148 &add_content2xml_hash($out_hash, "error", "macaddress");
1149 &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1);
1150 }
1151 }
1153 if (not $error) {
1155 # Get hostId
1156 $hostId = @{$msg_hash->{'hostId'}}[0];
1157 &add_content2xml_hash($out_hash, "hostId", "$hostId");
1159 # Load all products for this host with status != "not_installed" or actionRequest != "none"
1160 my $callobj = {
1161 method => 'getProductStates_hash',
1162 params => [ $hostId ],
1163 id => 1,
1164 };
1166 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1167 if (not &check_opsi_res($hres)){
1168 my $htmp= $hres->result->{$hostId};
1170 # check state != not_installed or action == setup -> load and add
1171 foreach my $product (@{$htmp}){
1172 # Now we've a couple of hashes...
1173 if ($product->{'installationStatus'} ne "not_installed" or
1174 $product->{'actionRequest'} ne "none"){
1176 # Do an action request for all these -> "setup".
1177 $callobj = {
1178 method => 'setProductActionRequest',
1179 params => [ $product->{'productId'}, $hostId, "setup" ],
1180 id => 1,
1181 };
1182 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1183 my ($res_err, $res_err_string) = &check_opsi_res($res);
1184 if ($res_err){
1185 &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1186 } else {
1187 &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1188 }
1189 }
1190 }
1191 }
1192 push(@out_msg_l, &create_xml_string($out_hash));
1195 # Build wakeup message for client
1196 if (not $error) {
1197 my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1198 &add_content2xml_hash($wakeup_hash, 'macAddress', $macaddress);
1199 my $wakeup_msg = &create_xml_string($wakeup_hash);
1200 push(@out_msg_l, $wakeup_msg);
1202 # invoke trigger wake for this gosa-si-server
1203 &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1204 }
1205 }
1207 # Return messages
1208 return @out_msg_l;
1209 }
1212 ## @method _set_action
1213 # Set action for an Opsi client
1214 # @param product - STRING - Opsi product
1215 # @param action - STRING - action
1216 # @param hostId - STRING - Opsi hostId
1217 sub _set_action {
1218 my $product= shift;
1219 my $action = shift;
1220 my $hostId = shift;
1221 my $callobj;
1223 $callobj = {
1224 method => 'setProductActionRequest',
1225 params => [ $product, $hostId, $action],
1226 id => 1,
1227 };
1229 $main::opsi_client->call($main::opsi_url, $callobj);
1230 }
1232 ## @method _set_state
1233 # Set state for an Opsi client
1234 # @param product - STRING - Opsi product
1235 # @param action - STRING - state
1236 # @param hostId - STRING - Opsi hostId
1237 sub _set_state {
1238 my $product = shift;
1239 my $state = shift;
1240 my $hostId = shift;
1241 my $callobj;
1243 $callobj = {
1244 method => 'setProductState',
1245 params => [ $product, $hostId, $state ],
1246 id => 1,
1247 };
1249 $main::opsi_client->call($main::opsi_url, $callobj);
1250 }
1252 1;