File Coverage

blib/lib/Monitoring/Icinga2/Client/Simple.pm
Criterion Covered Total %
statement 116 116 100.0
branch 35 38 100.0
condition 15 24 79.1
subroutine 30 30 100.0
pod 11 11 100.0
total 207 219 97.7


line stmt bran cond sub pod time code
1             # ABSTRACT: Simpler REST client for Icinga2
2              
3             package Monitoring::Icinga2::Client::Simple;
4             $Monitoring::Icinga2::Client::Simple::VERSION = '0.001000_09'; # TRIAL
5              
6 2     2   163984 $Monitoring::Icinga2::Client::Simple::VERSION = '0.00100009';use strict;
  2         17  
  2         58  
7 2     2   10 use warnings;
  2         7  
  2         61  
8 2     2   51 use 5.010_001;
  2         8  
9 2     2   970 use Monitoring::Icinga2::Client::REST 2;
  2         129950  
  2         88  
10 2     2   19 use parent -norequire, 'Monitoring::Icinga2::Client::REST';
  2         5  
  2         13  
11 2     2   86 use Carp;
  2         6  
  2         116  
12 2     2   14 use List::Util qw/ all any first /;
  2         6  
  2         246  
13 2     2   15 use constant DEBUG => $ENV{DEBUG};
  2         4  
  2         3715  
14              
15             sub new {
16 28     28 1 39450 my $class = shift;
17 28 100       323 croak( "only hash-style args are supported" ) if @_ % 2;
18 27         89 my %args = @_;
19             # uncoverable condition false
20 27   66     198 my $server = delete $args{server} // croak( "`server' arg is required" );
21 26         78 my $ua = delete $args{useragent};
22 26         120 my $self = $class->SUPER::new( $server, %args );
23 26 100       11043 if( defined $ua ) {
24             # This is a hack as I don't maintain the superclass. However, I wrote its
25             # constructor and we'll check whether it has changed so it should be fine.
26             # uncoverable branch true
27 25 50       78 defined $self->{ua} or croak( 'Monitoring::Icinga2::Client::REST seems to have changed internals; '. 'passing `useragent\' does not work. Please notify mbethke@cpan.org');
28 25         94 $ua->default_header( 'Accept' => 'application/json' );
29 25         755 $self->{ua} = $ua;
30             # uncoverable condition false
31             # uncoverable branch right
32 25   33     3239 $self->{_mics_author} = getlogin || getpwuid($<);
33             }
34 26         175 return $self;
35             }
36              
37             sub schedule_downtime {
38 6     6 1 439 my ($self, %args) = @_;
39 6         24 _checkargs(\%args, qw/ start_time end_time comment host /);
40             # uncoverable condition true
41 5   66     44 $args{author} //= $self->{_mics_author};
42              
43 5 100 100     23 if( $args{service} and not $args{services} ) {
44 1         6 return [ $self->_schedule_downtime_type( 'Service', \%args) ];
45             }
46              
47 4         8 delete $args{service}; # make sure _schedule_downtime_type doesn't set a wrong filter
48 4         14 my @results = $self->_schedule_downtime_type( 'Host', \%args );
49 4 100       16 push @results, $self->_schedule_downtime_type( 'Service', \%args ) if $args{services};
50 4         17 return \@results;
51             }
52              
53             sub _schedule_downtime_type {
54 7     7   18 my ($self, $type, $args) = @_;
55             my $req_results = $self->_verbose_request('POST',
56             '/actions/schedule-downtime',
57             {
58             type => $type,
59             joins => [ "host.name" ],
60             filter => _create_filter( $args ),
61 7         20 map { $_ => $args->{$_} } qw/ author start_time end_time comment duration fixed /
  42         122  
62             }
63             );
64 7         38 return @$req_results;
65             }
66              
67             sub remove_downtime {
68 3     3 1 205 my ($self, %args) = @_;
69              
70             defined $args{name}
71 3 100       16 and return $self->_remove_downtime_type( 'Downtime', "downtime=$args{name}" );
72              
73 2         9 _checkargs(\%args, 'host');
74              
75             defined $args{service}
76 2 100       13 and return $self->_remove_downtime_type( 'Service', \%args );
77              
78 1         6 return $self->_remove_downtime_type( 'Host', \%args );
79             }
80              
81             sub _remove_downtime_type {
82 3     3   10 my ($self, $type, $args) = @_;
83 3         6 my @post_args;
84              
85 3 100       10 if(ref $args) {
86 2         9 @post_args = (
87             undef,
88             {
89             type => $type,
90             joins => [ "host.name" ],
91             filter => _create_filter( $args ),
92             }
93             );
94             } else {
95 1         5 @post_args = ( $args, { type => $type } );
96             }
97 3         9 my $req_results = $self->_verbose_request('POST',
98             "/actions/remove-downtime",
99             @post_args,
100             );
101 3         14 return $req_results;
102             }
103              
104             sub send_custom_notification {
105 3     3 1 209 my ($self, %args) = @_;
106 3         11 _checkargs(\%args, qw/ comment /);
107 3         19 _checkargs_any(\%args, qw/ host service /);
108              
109 3 100       13 my $obj_type = defined $args{host} ? 'host' : 'service';
110              
111             return $self->_verbose_request('POST',
112             '/actions/send-custom-notification',
113             {
114             type => ucfirst $obj_type,
115             filter => "$obj_type.name==\"$args{$obj_type}\"",
116             comment => $args{comment},
117             # uncoverable condition false
118             # uncoverable branch right
119             author => $args{author} // $self->{_mics_author},
120             }
121 3   66     30 );
122             }
123              
124             sub set_notifications {
125 4     4 1 280 my ($self, %args) = @_;
126 4         16 _checkargs(\%args, qw/ state /);
127 3         19 _checkargs_any(\%args, qw/ host service /);
128 3 100       18 my $uri_object = $args{service} ? 'services' : 'hosts';
129              
130             return $self->_verbose_request('POST',
131             "/objects/$uri_object",
132             {
133             attrs => { enable_notifications => !!$args{state} },
134 3         23 filter => _create_filter( \%args ),
135             }
136             );
137             }
138              
139             sub query_app_attrs {
140 1     1 1 69 my ($self) = @_;
141              
142 1         5 my $r = $self->_verbose_request('GET',
143             "/status/IcingaApplication",
144             );
145             # uncoverable branch true
146             # uncoverable condition left
147             # uncoverable condition right
148 1 50 33     15 ref $r eq 'ARRAY' and defined $r->[0] and defined $r->[0]{status}{icingaapplication}{app} or die "Invalid result from Icinga";
      33        
149              
150 1         6 return $r->[0]{status}{icingaapplication}{app};
151             }
152              
153             {
154             my %legal_attrs = map { $_ => 1 } qw/
155             event_handlers
156             flapping
157             host_checks
158             notifications
159             perfdata
160             service_checks
161             /;
162              
163             sub set_app_attrs {
164 4     4 1 219 my ($self, %args) = @_;
165 4         21 _checkargs_any(\%args, keys %legal_attrs);
166 3         35 my @unknown_attrs = grep { not exists $legal_attrs{$_} } sort keys %args;
  7         18  
167 3 100       111 @unknown_attrs and croak(
168             sprintf "Unknown attributes: %s; legal attributes are: %s",
169             join(",", @unknown_attrs),
170             join(",", sort keys %legal_attrs),
171             );
172              
173             return $self->_verbose_request('POST',
174             '/objects/icingaapplications/app',
175             {
176             attrs => {
177 2         10 map { 'enable_' . $_ => !!$args{$_} } keys %args
  4         21  
178             },
179             }
180             );
181             }
182             }
183              
184             sub set_global_notifications {
185 1     1 1 79 my ($self, $state) = @_;
186 1         4 $self->set_app_attrs( notifications => $state );
187             }
188              
189             sub query_host {
190 1     1 1 71 my ($self, %args) = @_;
191 1         6 _checkargs(\%args, qw/ host /);
192 1         8 return $self->_verbose_request('GET',
193             '/objects/hosts',
194             { filter => "host.name==\"$args{host}\"" }
195             )->[0];
196             }
197              
198             sub query_child_hosts {
199 1     1 1 69 my ($self, %args) = @_;
200 1         5 _checkargs(\%args, qw/ host /);
201 1         10 return $self->_verbose_request('GET',
202             '/objects/hosts',
203             { filter => "\"$args{host}\" in host.vars.parents" }
204             );
205             }
206              
207             sub query_services {
208 1     1 1 71 my ($self, %args) = @_;
209 1         5 _checkargs(\%args, qw/ service /);
210 1         9 return $self->_verbose_request('GET',
211             '/objects/services',
212             { filter => "service.name==\"$args{service}\"" }
213             );
214             }
215              
216             sub _verbose_request {
217 21     21   56 my ($self, $method, $url, $getargs, $postdata) = @_;
218              
219 21 100 100     88 if(defined $getargs and ref $getargs) {
220             # getargs must be a string. if it ain't, it's actually postdata
221 17         30 $postdata = $getargs;
222 17         35 undef $getargs;
223             }
224 21 100       83 _debug("Query URL: $url", $getargs ? "Query args: $getargs" : ());
225 21         53 _debug_dump($postdata);
226             # uncoverable branch true
227 21 50       75 my $r = $self->do_request($method, $url, $getargs, $postdata)
228             or die $self->request_status_line . "\n";
229 21         14842 _debug_dump($r);
230 21         69 return $r->{results};
231             }
232              
233             # Make sure at all keys are defined in the hash referenced by the first arg
234             # Not a method!
235             sub _checkargs {
236 18     18   39 my $args = shift;
237              
238 33     33   122 all { defined $args->{$_} } @_ or croak(
239             sprintf "missing or undefined argument `%s' to %s()",
240 18 100   2   79 ( first { not defined $args->{$_} } @_ ),
  2         255  
241             (caller(1))[3]
242             );
243             }
244              
245             # Make sure at least one key is defined in the hash referenced by the first arg
246             # Not a method!
247             sub _checkargs_any {
248 10     10   19 my $args = shift;
249              
250 10 100   9   36 any { defined $args->{$_} } grep { exists $args->{$_} } @_ or croak(
  9         27  
  36         221  
251             sprintf "need at least one argument of: %s to %s()",
252             join(',', @_), (caller(1))[3]
253             );
254             }
255              
256             # Create a filter for a hostname in $args->{host} and optionally a service name in $args->{service}
257             # Not a method!
258             sub _create_filter {
259 12     12   24 my $args = shift;
260 12 100       155 croak( "`host' argument missing" ) unless defined $args->{host};
261 11         30 my $filter = "host.name==\"$args->{host}\"";
262 11 100       34 $filter .= " && service.name==\"$args->{service}\"" if $args->{service};
263 11         40 return $filter;
264             }
265              
266             sub _debug {
267 21     21   36 print STDERR @_ if DEBUG;
268             }
269              
270             sub _debug_dump {
271 42     42   73 if(DEBUG) {
272             require YAML::XS;
273             print YAML::XS::Dump(\@_);
274             }
275             }
276              
277             1;
278              
279             __END__