Code

update: opsi_com complete with sanity checks and doxygen lines
[gosa.git] / gosa-si / server / events / opsi_com.pm
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) );
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) );
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         }
294     }
296     # Return message
297     return ( &create_xml_string($out_hash) );
300 ## @method opsi_modify_client
301 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
302 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
303 # @param msg_hash - HASHREF - message information parsed into a hash
304 # @param session_id - INTEGER - POE session id of the processing of this message    
305 # @return out_msg - STRING - feedback to GOsa in success and error case
306 sub opsi_modify_client {
307     my ($msg, $msg_hash, $session_id) = @_;
308     my $header = @{$msg_hash->{'header'}}[0];
309     my $source = @{$msg_hash->{'source'}}[0];
310     my $target = @{$msg_hash->{'target'}}[0];
311     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
312     my $hostId;
313     my $error = 0;
314     my ($sres, $sres_err, $sres_err_string);
316     # Build return message with twisted target and source
317     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
318     if (defined $forward_to_gosa) {
319         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
320     }
322     # Sanity check of needed parameter
323     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1))  {
324         $error++;
325         &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
326         &add_content2xml_hash($out_hash, "error", "hostId");
327         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
328     }
330     if (not $error) {
331         # Get hostID
332         $hostId = @{$msg_hash->{'hostId'}}[0];
333         &add_content2xml_hash($out_hash, "hostId", $hostId);
334         my $name= $hostId;
335         $name=~ s/^([^.]+).*$/$1/;
336         my $domain= $hostId;
337         $domain=~ s/^[^.]+(.*)$/$1/;
339         # Modify description, notes or mac if defined
340         my ($description, $notes, $mac);
341         my $callobj;
342         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
343             $description = @{$msg_hash->{'description'}}[0];
344             $callobj = {
345                 method  => 'setHostDescription',
346                 params  => [ $hostId, $description ],
347                 id  => 1,
348             };
349             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
350             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
351             if ($sres_err){
352                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
353                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
354             }
355         }
356         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
357             $notes = @{$msg_hash->{'notes'}}[0];
358             $callobj = {
359                 method  => 'setHostNotes',
360                 params  => [ $hostId, $notes ],
361                 id  => 1,
362             };
363             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
364             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
365             if ($sres_err){
366                 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
367                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
368             }
369         }
370         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
371             $mac = @{$msg_hash->{'mac'}}[0];
372             $callobj = {
373                 method  => 'setMacAddress',
374                 params  => [ $hostId, $mac ],
375                 id  => 1,
376             };
377             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
378             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
379             if ($sres_err){
380                 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
381                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
382             }
383         }
384     }
386     # Return message
387     return ( &create_xml_string($out_hash) );
390     
391 ## @method opsi_get_netboot_products
392 # Get netboot products for specific host.
393 # @param msg - STRING - xml message with tag hostId
394 # @param msg_hash - HASHREF - message information parsed into a hash
395 # @param session_id - INTEGER - POE session id of the processing of this message
396 # @return out_msg - STRING - feedback to GOsa in success and error case
397 sub opsi_get_netboot_products {
398     my ($msg, $msg_hash, $session_id) = @_;
399     my $header = @{$msg_hash->{'header'}}[0];
400     my $source = @{$msg_hash->{'source'}}[0];
401     my $target = @{$msg_hash->{'target'}}[0];
402     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
403     my $hostId;
404     my $error = 0;
405     my $xml_msg;
407     # Build return message with twisted target and source
408     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
409     if (defined $forward_to_gosa) {
410         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
411     }
413     # Sanity check of needed parameter
414     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1))  {
415         $error++;
416         &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
417         &add_content2xml_hash($out_hash, "error", "hostId");
418         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
419     }
421     if (not $error) {
423         # Get hostID if defined
424         $hostId = @{$msg_hash->{'hostId'}}[0];
425         &add_content2xml_hash($out_hash, "hostId", $hostId);
427         &add_content2xml_hash($out_hash, "xxx", "");
428         $xml_msg= &create_xml_string($out_hash);
430         # For hosts, only return the products that are or get installed
431         my $callobj;
432         $callobj = {
433             method  => 'getNetBootProductIds_list',
434             params  => [ ],
435             id  => 1,
436         };
438         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
439         my %r = ();
440         for (@{$res->result}) { $r{$_} = 1 }
442         if (not &check_opsi_res($res)){
444             if (defined $hostId){
445                 $callobj = {
446                     method  => 'getProductStates_hash',
447                     params  => [ $hostId ],
448                     id  => 1,
449                 };
451                 my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
452                 if (not &check_opsi_res($hres)){
453                     my $htmp= $hres->result->{$hostId};
455                     # check state != not_installed or action == setup -> load and add
456                     foreach my $product (@{$htmp}){
458                         if (!defined ($r{$product->{'productId'}})){
459                             next;
460                         }
462                         # Now we've a couple of hashes...
463                         if ($product->{'installationStatus'} ne "not_installed" or
464                                 $product->{'actionRequest'} eq "setup"){
465                             my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
467                             $callobj = {
468                                 method  => 'getProduct_hash',
469                                 params  => [ $product->{'productId'} ],
470                                 id  => 1,
471                             };
473                             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
474                             if (not &check_opsi_res($sres)){
475                                 my $tres= $sres->result;
477                                 my $name= xml_quote($tres->{'name'});
478                                 my $r= $product->{'productId'};
479                                 my $description= xml_quote($tres->{'description'});
480                                 $name=~ s/\//\\\//;
481                                 $description=~ s/\//\\\//;
482                                 $xml_msg=~ s/<xxx><\/xxx>/<item><productId>$r<\/productId><name><\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
483                             }
484                         }
485                     }
487                 }
489             } else {
490                 foreach my $r (@{$res->result}) {
491                     $callobj = {
492                         method  => 'getProduct_hash',
493                         params  => [ $r ],
494                         id  => 1,
495                     };
497                     my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
498                     if (not &check_opsi_res($sres)){
499                         my $tres= $sres->result;
501                         my $name= xml_quote($tres->{'name'});
502                         my $description= xml_quote($tres->{'description'});
503                         $name=~ s/\//\\\//;
504                         $description=~ s/\//\\\//;
505                         $xml_msg=~ s/<xxx><\/xxx>/<item><productId>$r<\/productId><name><\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
506                     }
507                 }
509             }
510         }
511         $xml_msg=~ s/<xxx><\/xxx>//;
512     }
514     # Return message
515     return ( $xml_msg );
519 ## @method opsi_get_product_properties
520 # Get product properties for a product and a specific host or gobally for a product.
521 # @param msg - STRING - xml message with tags productId and optional hostId
522 # @param msg_hash - HASHREF - message information parsed into a hash
523 # @param session_id - INTEGER - POE session id of the processing of this message
524 # @return out_msg - STRING - feedback to GOsa in success and error case
525 sub opsi_get_product_properties {
526     my ($msg, $msg_hash, $session_id) = @_;
527     my $header = @{$msg_hash->{'header'}}[0];
528     my $source = @{$msg_hash->{'source'}}[0];
529     my $target = @{$msg_hash->{'target'}}[0];
530     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
531     my ($hostId, $productId);
532     my $error = 0;
533     my $xml_msg;
535     # build return message with twisted target and source
536     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
537     if (defined $forward_to_gosa) {
538         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
539     }
541     # Sanity check of needed parameter
542     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1))  {
543         $error++;
544         &add_content2xml_hash($out_hash, "productId_error", "no productId specified or productId tag invalid");
545         &add_content2xml_hash($out_hash, "error", "productId");
546         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
547     }
549     if (not $error) {
551         # Get productid
552         $productId = @{$msg_hash->{'productId'}}[0];
553         &add_content2xml_hash($out_hash, "producId", "$productId");
556         $hostId = @{$msg_hash->{'hostId'}}[0];
557         &add_content2xml_hash($out_hash, "hostId", $hostId);
559         # Load actions
560         my $callobj = {
561             method  => 'getPossibleProductActions_list',
562             params  => [ $productId ],
563             id  => 1,
564         };
565         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
566         if (not &check_opsi_res($res)){
567             foreach my $action (@{$res->result}){
568                 &add_content2xml_hash($out_hash, "action", $action);
569             }
570         }
572         # Add place holder
573         &add_content2xml_hash($out_hash, "xxx", "");
575     }
577     # Move to XML string
578     $xml_msg= &create_xml_string($out_hash);
580     if (not $error) {
582         # JSON Query
583         my $callobj = {
584             method  => 'getProductProperties_hash',
585             params  => [ $productId ],
586             id  => 1,
587         };
588         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
589         if (not &check_opsi_res($res)){
590             my $r= $res->result;
591             foreach my $key (keys %{$r}) {
592                 my $item= "<item>";
593                 my $value= $r->{$key};
594                 if (UNIVERSAL::isa( $value, "ARRAY" )){
595                     foreach my $subval (@{$value}){
596                         $item.= "<$key>".xml_quote($subval)."</$key>";
597                     }
598                 } else {
599                     $item.= "<$key>".xml_quote($value)."</$key>";
600                 }
601                 $item.= "</item>";
602                 $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
603             }
604         }
606         $xml_msg=~ s/<xxx><\/xxx>//;
607     }
609     # Return message
610     return ( $xml_msg );
614 ## @method opsi_set_product_properties
615 # 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.
616 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
617 # @param msg_hash - HASHREF - message information parsed into a hash
618 # @param session_id - INTEGER - POE session id of the processing of this message
619 # @return out_msg - STRING - feedback to GOsa in success and error case
620 sub opsi_set_product_properties {
621     my ($msg, $msg_hash, $session_id) = @_;
622     my $header = @{$msg_hash->{'header'}}[0];
623     my $source = @{$msg_hash->{'source'}}[0];
624     my $target = @{$msg_hash->{'target'}}[0];
625     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
626     my ($productId, $hostId);
627     my $error = 0;
629     # Build return message with twisted target and source
630     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
631     if (defined $forward_to_gosa) {
632         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
633     }
635     # Sanity check of needed parameter
636     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1))  {
637         $error++;
638         &add_content2xml_hash($out_hash, "productId_error", "no productId specified or productId tag invalid");
639         &add_content2xml_hash($out_hash, "error", "productId");
640         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
641     }
642     if ((not exists $msg_hash->{'item'}) || (@{$msg_hash->{'item'}}[0] != 1)) {
643         $error++;
644         &add_content2xml_hash($out_hash, "item_error", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
645         &add_content2xml_hash($out_hash, "error", "item");
646         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
647     } else {
648         if ((not exists $msg_hash->{'item'}->{'name'}) || (@{$msg_hash->{'item'}->{'name'}}[0] != 1)) {
649             $error++;
650             &add_content2xml_hash($out_hash, "name_error", "message needs within the xml-tag 'item' one xml-tags 'name'");
651             &add_content2xml_hash($out_hash, "error", "name");
652             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
653         }
654         if ((not exists $msg_hash->{'item'}->{'value'}) || (@{$msg_hash->{'item'}->{'value'}}[0] != 1)) {
655             $error++;
656             &add_content2xml_hash($out_hash, "value_error", "message needs within the xml-tag 'item' one xml-tags 'value'");
657             &add_content2xml_hash($out_hash, "error", "value");
658             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
659         }
660     }
661     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1))  {
662         $error++;
663         &add_content2xml_hash($out_hash, "hostId_error", "hostId contains no or more than one values");
664         &add_content2xml_hash($out_hash, "error", "hostId");
665         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
666     }
668     if (not $error) {
669         
670     # Get productId
671         $productId =  @{$msg_hash->{'productId'}}[0];
672         &add_content2xml_hash($out_hash, "productId", $productId);
674     # Get hostID if defined
675         if (exists $msg_hash->{'hostId'}){
676             $hostId = @{$msg_hash->{'hostId'}}[0];
677             &add_content2xml_hash($out_hash, "hostId", $hostId);
678         }
680     # Set product states if requested
681         if (defined @{$msg_hash->{'action'}}[0]){
682             &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
683         }
684         if (defined @{$msg_hash->{'state'}}[0]){
685             &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
686         }
688     # Find properties
689         foreach my $item (@{$msg_hash->{'item'}}){
690             # JSON Query
691             my $callobj;
693             if (defined $hostId){
694                 $callobj = {
695                     method  => 'setProductProperty',
696                     params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
697                     id  => 1,
698                 };
699             } else {
700                 $callobj = {
701                     method  => 'setProductProperty',
702                     params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
703                     id  => 1,
704                 };
705             }
707             my $res = $main::opsi_client->call($main::opsi_url, $callobj);
708             my ($res_err, $res_err_string) = &check_opsi_res($res);
709 # TODO : This error message sounds strange
710             if ($res_err){
711                 &man::daemon_log("$session_id ERROR: no communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
712                 &add_content2xml_hash($out_hash, "error", $res_err_string);
713             }
714         }
716     }
718     # Return message
719     return ( &create_xml_string($out_hash) );
723 ## @method opsi_get_client_hardware
724 # Reports client hardware inventory.
725 # @param msg - STRING - xml message with tag hostId
726 # @param msg_hash - HASHREF - message information parsed into a hash
727 # @param session_id - INTEGER - POE session id of the processing of this message
728 # @return out_msg - STRING - feedback to GOsa in success and error case
729 sub opsi_get_client_hardware {
730     my ($msg, $msg_hash, $session_id) = @_;
731     my $header = @{$msg_hash->{'header'}}[0];
732     my $source = @{$msg_hash->{'source'}}[0];
733     my $target = @{$msg_hash->{'target'}}[0];
734     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
735     my $hostId;
736     my $error = 0;
737     my $xml_msg;
739     # build return message with twisted target and source
740     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
741     if (defined $forward_to_gosa) {
742       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
743     }
745     # Sanity check of needed parameter
746     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1))  {
747         $error++;
748         &add_content2xml_hash($out_hash, "hostId_error", "hostId contains no or more than one values");
749         &add_content2xml_hash($out_hash, "error", "hostId");
750         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
751     }
753     if (not $error) {
755     # Get hostID
756         $hostId = @{$msg_hash->{'hostId'}}[0];
757         &add_content2xml_hash($out_hash, "hostId", "$hostId");
758         &add_content2xml_hash($out_hash, "xxx", "");
759     }    
761     # Move to XML string
762     $xml_msg= &create_xml_string($out_hash);
763     
764     if (not $error) {
766     # JSON Query
767         my $callobj = {
768             method  => 'getHardwareInformation_hash',
769             params  => [ $hostId ],
770             id  => 1,
771         };
773         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
774         if (not &check_opsi_res($res)){
775             my $result= $res->result;
776             foreach my $r (keys %{$result}){
777                 my $item= "<item><id>".xml_quote($r)."</id>";
778                 my $value= $result->{$r};
779                 foreach my $sres (@{$value}){
781                     foreach my $dres (keys %{$sres}){
782                         if (defined $sres->{$dres}){
783                             $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
784                         }
785                     }
787                 }
788                 $item.= "</item>";
789                 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
791             }
792         }
794         $xml_msg=~ s/<xxx><\/xxx>//;
796     }
798     # Return message
799     return ( $xml_msg );
803 ## @method opsi_list_clients
804 # Reports all Opsi clients. 
805 # @param msg - STRING - xml message 
806 # @param msg_hash - HASHREF - message information parsed into a hash
807 # @param session_id - INTEGER - POE session id of the processing of this message
808 # @return out_msg - STRING - feedback to GOsa in success and error case
809 sub opsi_list_clients {
810     my ($msg, $msg_hash, $session_id) = @_;
811     my $header = @{$msg_hash->{'header'}}[0];
812     my $source = @{$msg_hash->{'source'}}[0];
813     my $target = @{$msg_hash->{'target'}}[0];
814     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
815     my $error = 0;
817     # build return message with twisted target and source
818     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
819     if (defined $forward_to_gosa) {
820       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
821     }
822     &add_content2xml_hash($out_hash, "xxx", "");
824     # Move to XML string
825     my $xml_msg= &create_xml_string($out_hash);
827     if (not $error) {
829     # JSON Query
830         my $callobj = {
831             method  => 'getClients_listOfHashes',
832             params  => [ ],
833             id  => 1,
834         };
835         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
836         if (not &check_opsi_res($res)){
837             foreach my $host (@{$res->result}){
838                 my $item= "<item><name>".$host->{'hostId'}."</name>";
839                 if (defined($host->{'description'})){
840                     $item.= "<description>".xml_quote($host->{'description'})."</description>";
841                 }
842                 if (defined($host->{'ip'})){
843                     $item.= "<ip>".xml_quote($host->{'ip'})."</ip>";
844                 }
845                 if (defined($host->{'mac'})){
846                     $item.= "<mac>".xml_quote($host->{'mac'})."</mac>";
847                 }
848                 if (defined($host->{'notes'})){
849                     $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
850                 }
851                 $item.= "</item>";
852                 $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
853             }
854         }
855     }
857     $xml_msg=~ s/<xxx><\/xxx>//;
858     return ( $xml_msg );
862 ## @method opsi_get_client_software
863 # Reports client software inventory.
864 # @param msg - STRING - xml message with tag hostId
865 # @param msg_hash - HASHREF - message information parsed into a hash
866 # @param session_id - INTEGER - POE session id of the processing of this message
867 # @return out_msg - STRING - feedback to GOsa in success and error case
868 sub opsi_get_client_software {
869     my ($msg, $msg_hash, $session_id) = @_;
870     my $header = @{$msg_hash->{'header'}}[0];
871     my $source = @{$msg_hash->{'source'}}[0];
872     my $target = @{$msg_hash->{'target'}}[0];
873     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
874     my $error = 0;
875     my $hostId;
876     my $xml_msg;
878     # build return message with twisted target and source
879     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
880     if (defined $forward_to_gosa) {
881       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
882     }
884     # Sanity check of needed parameter
885     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1))  {
886         $error++;
887         &add_content2xml_hash($out_hash, "hostId_error", "hostId contains no or more than one values");
888         &add_content2xml_hash($out_hash, "error", "hostId");
889         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
890     }
892     if (not $error) {
894     # Get hostID
895         $hostId = @{$msg_hash->{'hostId'}}[0];
896         &add_content2xml_hash($out_hash, "hostId", "$hostId");
897         &add_content2xml_hash($out_hash, "xxx", "");
898     }
900     $xml_msg= &create_xml_string($out_hash);
902     if (not $error) {
904     # JSON Query
905         my $callobj = {
906             method  => 'getSoftwareInformation_hash',
907             params  => [ $hostId ],
908             id  => 1,
909         };
911         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
912         if (not &check_opsi_res($res)){
913             my $result= $res->result;
914 # TODO : fertig???   
915         }
917         $xml_msg=~ s/<xxx><\/xxx>//;
919     }
921     # Return message
922     return ( $xml_msg );
926 ## @method opsi_get_local_products
927 # Reports product for given hostId or globally.
928 # @param msg - STRING - xml message with optional tag hostId
929 # @param msg_hash - HASHREF - message information parsed into a hash
930 # @param session_id - INTEGER - POE session id of the processing of this message
931 # @return out_msg - STRING - feedback to GOsa in success and error case
932 sub opsi_get_local_products {
933     my ($msg, $msg_hash, $session_id) = @_;
934     my $header = @{$msg_hash->{'header'}}[0];
935     my $source = @{$msg_hash->{'source'}}[0];
936     my $target = @{$msg_hash->{'target'}}[0];
937     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
938     my $hostId;
939     my $error = 0;
940     my $xml_msg;
942     # Build return message with twisted target and source
943     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
944     if (defined $forward_to_gosa) {
945         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
946     }
947     &add_content2xml_hash($out_hash, "xxx", "");
949     # Get hostID if defined
950     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
951         $hostId = @{$msg_hash->{'hostId'}}[0];
952         &add_content2xml_hash($out_hash, "hostId", $hostId);
953     }
955     # Move to XML string
956     my $xml_msg= &create_xml_string($out_hash);
958     # For hosts, only return the products that are or get installed
959     my $callobj;
960     $callobj = {
961         method  => 'getLocalBootProductIds_list',
962         params  => [ ],
963         id  => 1,
964     };
966     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
967     my %r = ();
968     for (@{$res->result}) { $r{$_} = 1 }
970     if (not &check_opsi_res($res)){
972         if (defined $hostId){
973             $callobj = {
974                 method  => 'getProductStates_hash',
975                 params  => [ $hostId ],
976                 id  => 1,
977             };
979             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
980             if (not &check_opsi_res($hres)){
981                 my $htmp= $hres->result->{$hostId};
983                 # Check state != not_installed or action == setup -> load and add
984                 foreach my $product (@{$htmp}){
986                     if (!defined ($r{$product->{'productId'}})){
987                         next;
988                     }
990                     # Now we've a couple of hashes...
991                     if ($product->{'installationStatus'} ne "not_installed" or
992                             $product->{'actionRequest'} eq "setup"){
993                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
995                         $callobj = {
996                             method  => 'getProduct_hash',
997                             params  => [ $product->{'productId'} ],
998                             id  => 1,
999                         };
1001                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1002                         if (not &check_opsi_res($sres)){
1003                             my $tres= $sres->result;
1005                             my $name= xml_quote($tres->{'name'});
1006                             my $r= $product->{'productId'};
1007                             my $description= xml_quote($tres->{'description'});
1008                             $name=~ s/\//\\\//;
1009                             $description=~ s/\//\\\//;
1010                             $xml_msg=~ s/<xxx><\/xxx>/<item><productId>$r<\/productId><name><\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1011                         }
1013                     }
1014                 }
1016             }
1018         } else {
1019             foreach my $r (@{$res->result}) {
1020                 $callobj = {
1021                     method  => 'getProduct_hash',
1022                     params  => [ $r ],
1023                     id  => 1,
1024                 };
1026                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1027                 if (not &check_opsi_res($sres)){
1028                     my $tres= $sres->result;
1030                     my $name= xml_quote($tres->{'name'});
1031                     my $description= xml_quote($tres->{'description'});
1032                     $name=~ s/\//\\\//;
1033                     $description=~ s/\//\\\//;
1034                     $xml_msg=~ s/<xxx><\/xxx>/<item><productId>$r<\/productId><name><\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1035                 }
1037             }
1039         }
1040     }
1042     $xml_msg=~ s/<xxx><\/xxx>//;
1044     # Retrun Message
1045     return ( $xml_msg );
1049 ## @method opsi_del_client
1050 # Deletes a client from Opsi.
1051 # @param msg - STRING - xml message with tag hostId
1052 # @param msg_hash - HASHREF - message information parsed into a hash
1053 # @param session_id - INTEGER - POE session id of the processing of this message
1054 # @return out_msg - STRING - feedback to GOsa in success and error case
1055 sub opsi_del_client {
1056     my ($msg, $msg_hash, $session_id) = @_;
1057     my $header = @{$msg_hash->{'header'}}[0];
1058     my $source = @{$msg_hash->{'source'}}[0];
1059     my $target = @{$msg_hash->{'target'}}[0];
1060     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1061     my $hostId;
1062     my $error = 0;
1064     # Build return message with twisted target and source
1065     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1066     if (defined $forward_to_gosa) {
1067       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1068     }
1070     # Sanity check of needed parameter
1071     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1))  {
1072         $error++;
1073         &add_content2xml_hash($out_hash, "hostId_error", "hostId contains no or more than one values");
1074         &add_content2xml_hash($out_hash, "error", "hostId");
1075         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1076     }
1078     if (not $error) {
1080     # Get hostID
1081         $hostId = @{$msg_hash->{'hostId'}}[0];
1082         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1084     # JSON Query
1085         my $callobj = {
1086             method  => 'deleteClient',
1087             params  => [ $hostId ],
1088             id  => 1,
1089         };
1090         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1091     }
1093     # Move to XML string
1094     my $xml_msg= &create_xml_string($out_hash);
1096     # Return message
1097     return ( $xml_msg );
1101 ## @method opsi_install_client
1102 # Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1103 # @param msg - STRING - xml message with tags hostId, macaddress
1104 # @param msg_hash - HASHREF - message information parsed into a hash
1105 # @param session_id - INTEGER - POE session id of the processing of this message
1106 # @return out_msg - STRING - feedback to GOsa in success and error case
1107 sub opsi_install_client {
1108     my ($msg, $msg_hash, $session_id) = @_;
1109     my $header = @{$msg_hash->{'header'}}[0];
1110     my $source = @{$msg_hash->{'source'}}[0];
1111     my $target = @{$msg_hash->{'target'}}[0];
1112     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1115     my ($hostId, $macaddress);
1117     my $error = 0;
1118     my @out_msg_l;
1120     # Build return message with twisted target and source
1121     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1122     if (defined $forward_to_gosa) {
1123         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1124     }
1126     # Sanity check of needed parameter
1127     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1))  {
1128         $error++;
1129         &add_content2xml_hash($out_hash, "hostId_error", "no hostId specified or hostId tag invalid");
1130         &add_content2xml_hash($out_hash, "error", "hostId");
1131         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1132     }
1133     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1))  {
1134         $error++;
1135         &add_content2xml_hash($out_hash, "macaddress_error", "no macaddress specified or macaddress tag invalid");
1136         &add_content2xml_hash($out_hash, "error", "macaddress");
1137         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1138     } else {
1139         if ((exists $msg_hash->{'macaddress'}) && 
1140                 ($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)) {  
1141             $macaddress = $1; 
1142         } else { 
1143             $error ++; 
1144             &add_content2xml_hash($out_hash, "macaddress_error", "given mac address is not correct");
1145             &add_content2xml_hash($out_hash, "error", "macaddress");
1146             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1147         }
1148     }
1150     if (not $error) {
1152     # Get hostId
1153         $hostId = @{$msg_hash->{'hostId'}}[0];
1154         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1156         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1157         my $callobj = {
1158             method  => 'getProductStates_hash',
1159             params  => [ $hostId ],
1160             id  => 1,
1161         };
1163         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1164         if (not &check_opsi_res($hres)){
1165             my $htmp= $hres->result->{$hostId};
1167             # check state != not_installed or action == setup -> load and add
1168             foreach my $product (@{$htmp}){
1169                 # Now we've a couple of hashes...
1170                 if ($product->{'installationStatus'} ne "not_installed" or
1171                         $product->{'actionRequest'} ne "none"){
1173                     # Do an action request for all these -> "setup".
1174                     $callobj = {
1175                         method  => 'setProductActionRequest',
1176                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1177                         id  => 1,
1178                     };
1179                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1180                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1181                     if ($res_err){
1182                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1183                     } else {
1184                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1185                     }
1186                 }
1187             }
1188         }
1189         push(@out_msg_l, &create_xml_string($out_hash));
1190     
1192     # Build wakeup message for client
1193         if (not $error) {
1194             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1195             &add_content2xml_hash($wakeup_hash, 'macAddress', $macaddress);
1196             my $wakeup_msg = &create_xml_string($wakeup_hash);
1197             push(@out_msg_l, $wakeup_msg);
1199             # invoke trigger wake for this gosa-si-server
1200             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1201         }
1202     }
1203     
1204     # Return messages
1205     return @out_msg_l;
1209 ## @method _set_action
1210 # Set action for an Opsi client
1211 # @param product - STRING - Opsi product
1212 # @param action - STRING - action
1213 # @param hostId - STRING - Opsi hostId
1214 sub _set_action {
1215   my $product= shift;
1216   my $action = shift;
1217   my $hostId = shift;
1218   my $callobj;
1220   $callobj = {
1221     method  => 'setProductActionRequest',
1222     params  => [ $product, $hostId, $action],
1223     id  => 1,
1224   };
1226   $main::opsi_client->call($main::opsi_url, $callobj);
1229 ## @method _set_state
1230 # Set state for an Opsi client
1231 # @param product - STRING - Opsi product
1232 # @param action - STRING - state
1233 # @param hostId - STRING - Opsi hostId
1234 sub _set_state {
1235   my $product = shift;
1236   my $state = shift;
1237   my $hostId = shift;
1238   my $callobj;
1240   $callobj = {
1241     method  => 'setProductState',
1242     params  => [ $product, $hostId, $state ],
1243     id  => 1,
1244   };
1246   $main::opsi_client->call($main::opsi_url, $callobj);
1249 1;