File Coverage

blib/lib/Check/supervisorctl.pm
Criterion Covered Total %
statement 11 132 8.3
branch 0 70 0.0
condition 0 6 0.0
subroutine 4 6 66.6
pod 2 2 100.0
total 17 216 7.8


line stmt bran cond sub pod time code
1             package Check::supervisorctl;
2              
3 1     1   123845 use 5.006;
  1         4  
4 1     1   5 use strict;
  1         2  
  1         32  
5 1     1   3 use warnings;
  1         3  
  1         59  
6 1     1   611 use File::Slurp qw(read_dir);
  1         41880  
  1         1440  
7              
8             =head1 NAME
9              
10             Check::supervisorctl - Check the status of supervisorctl to see if it is okay.
11              
12             =head1 VERSION
13              
14             Version 0.0.1
15              
16             =cut
17              
18             our $VERSION = '0.0.1';
19              
20             =head1 SYNOPSIS
21              
22             use Check::supervisorctl;
23              
24             my $check_supervisorctl = Check::supervisorctl->new();
25             ...
26              
27             =head1 SUBROUTINES/METHODS
28              
29             =head2 new
30              
31             Initiates the object.
32              
33             - status_mapping :: A hash of status mapping values.
34             default :: {
35             stopped => 2,
36             stopped => 2,
37             starting => 0,
38             running => 0,
39             backoff => 2,
40             stopping => 2,
41             exited => 2,
42             fatal => 2,
43             unknown => 2,
44             }
45              
46             - not_running_val :: Value for if it a config is not running.
47             default :: 2
48              
49             - config_missing_val :: Value for if it a config is missing for a running item.
50             default :: 2
51              
52             - config_dir_missing_val :: Value for if it the config dir is not present.
53             default :: 3
54              
55             - config_dir_nonreadable_val :: Value for if it the config dir is not readable.
56             default :: 3
57              
58             - config_check :: Boolean for if it should check the configs or not.
59             default :: 0
60              
61             - ignore :: A array of running items to ignore.
62             default :: []
63              
64             - config_ignore :: A array of configs to ignore.
65             default :: []
66              
67             - config_dir :: Config dir path.
68             default :: /usr/local/etc/supervisor/conf.d
69             default Linux :: /etc/supervisor/conf.d
70              
71             =cut
72              
73             sub new {
74 0     0 1   my ( $blank, %opts ) = @_;
75              
76 0           my $self = {
77             status_mapping => {
78             stopped => 2,
79             starting => 0,
80             running => 0,
81             backoff => 2,
82             stopping => 2,
83             exited => 2,
84             fatal => 2,
85             unknown => 2,
86             },
87             val_to_string => {
88             0 => 'OK',
89             1 => 'WARNING',
90             2 => 'ALERT',
91             3 => 'UNKNOWN'
92             },
93             not_running_val => 2,
94             config_missing_val => 2,
95             config_dir_missing_val => 3,
96             config_dir_nonreadable_val => 3,
97             config_check => 0,
98             ignore => {},
99             config_ignore => {},
100             config_dir => '/usr/local/etc/supervisor/conf.d',
101             };
102 0           bless $self;
103              
104 0 0         if ( $^O eq 'linux' ) {
105 0           $self->{config_dir} = '/etc/supervisor/conf.d';
106             }
107              
108             # read in ignore settings
109 0 0         if ( defined( $opts{ignore} ) ) {
110 0 0         if ( ref( $opts{ignore} ) ne 'ARRAY' ) {
111 0           die( '$opts{ignore} not a ref type of ARRAY but "' . ref( $opts{ignore} ) . '"' );
112             }
113 0           foreach my $to_ignore ( @{ $opts{ignore} } ) {
  0            
114 0 0         if ( ref($to_ignore) ne '' ) {
115 0           die( 'array $opts{ignore} contains a item that is not of ref type "" but ' . ref($to_ignore) );
116             }
117 0           $self->{ignore}{$to_ignore} = 1;
118             }
119             } ## end if ( defined( $opts{ignore} ) )
120              
121             # read in config ignore settings
122 0 0         if ( defined( $opts{config_ignore} ) ) {
123 0 0         if ( ref( $opts{config_ignore} ) ne 'ARRAY' ) {
124 0           die( '$opts{config_ignore} not a ref type of ARRAY but "' . ref( $opts{config_ignore} ) . '"' );
125             }
126 0           foreach my $to_ignore ( @{ $opts{config_ignore} } ) {
  0            
127 0 0         if ( ref($to_ignore) ne '' ) {
128 0           die( 'array $opts{config_ignore} contains a item that is not of ref type "" but ' . ref($to_ignore) );
129             }
130 0           $self->{config_ignore}{$to_ignore} = 1;
131             }
132             } ## end if ( defined( $opts{config_ignore} ) )
133              
134             # read in other status settings
135             my @other_status
136 0           = ( 'not_running_val', 'config_missing_val', 'config_dir_missing_val', 'config_dir_nonreadable_val' );
137 0           foreach my $to_read_in (@other_status) {
138 0 0         if ( defined( $opts{$to_read_in} ) ) {
139 0 0         if ( ref( $opts{$to_read_in} ) ne '' ) {
140 0           die( '$opts{' . $to_read_in . '} not a ref type of "" not "' . ref( $opts{to_read_in} ) . '"' );
141             }
142 0 0         if ( $opts{$to_read_in} !~ /^[0123]$/ ) {
143 0           die( '$opts{' . $to_read_in . '} is not 0, 1, 2, or 3, but "' . $opts{$to_read_in} . '"' );
144             }
145 0           $self->{$to_read_in} = $opts{$to_read_in};
146             }
147             } ## end foreach my $to_read_in (@other_status)
148              
149             # read in any specified status mappings and
150 0 0         if ( defined( $opts{status_mapping} ) ) {
151 0 0         if ( ref( $opts{status_mapping} ) ne 'HASH' ) {
152 0           die( '$opts{status_mapping} not a ref type of HASH but "' . ref( $opts{status_mapping} ) . '"' );
153             }
154 0           foreach my $status ( keys( %{ $opts{status_mapping} } ) ) {
  0            
155 0           my $lc_status = lc($status);
156 0 0         if ( ref( $opts{status_mapping}{$status} ) ne '' ) {
157             die( '$opts{status_mapping}{'
158             . $status
159             . '} is not of ref type "" but "'
160 0           . ref( $opts{status_mapping}{$status} )
161             . '"' );
162             }
163 0 0         if ( !defined( $self->{status_mapping}{$lc_status} ) ) {
164 0           die( "'"
165             . $status
166             . "' is not a known status type... expected stopped, starting, running backoff, stopping, exited, fatal, unknown"
167             );
168             }
169 0 0         if ( $opts{status_mapping}{$status} !~ /^[0123]$/ ) {
170             die( '$opts{status_mapping}{'
171             . $status
172             . '} is not 0, 1, 2, or 3, but "'
173 0           . $opts{status_mapping}{$status}
174             . '"' );
175             }
176 0           $self->{status_mapping}{$lc_status} = $opts{status_mapping}{$status};
177             } ## end foreach my $status ( keys( %{ $opts{status_mapping...}}))
178             } ## end if ( defined( $opts{status_mapping} ) )
179              
180 0 0         if ( defined( $opts{config_check} ) ) {
181 0 0         if ( ref( $opts{config_check} ) ne '' ) {
182 0           die( '$opts{config_check} is not of ref type "" but "' . ref( $opts{config_check} ) . '"' );
183             }
184 0           $self->{config_check} = $opts{config_check};
185 0 0         if ( defined( $opts{config_dir} ) ) {
186 0 0         if ( ref( $opts{config_dir} ) ne '' ) {
187 0           die( '$opts{config_dir} is not of ref type "" but "' . ref( $opts{config_dir} ) . '"' );
188             }
189 0           $self->{config_dir} = $opts{config_dir};
190             }
191             } ## end if ( defined( $opts{config_check} ) )
192              
193 0           return $self;
194             } ## end sub new
195              
196             =pod
197              
198             =head2 run
199              
200             This runs it.
201              
202             my $results=$check_supervisorctl->run;
203              
204             use Data::Dumper;
205             print Dumper($results);
206             exit($results->{exit});
207              
208             The returned data is as below.
209              
210             - .configs[] :: A array of configs found.
211              
212             - .configs_not_running[] :: A array of configs present but not running.
213              
214             - .config_missing[] :: A array of running items was found, but no matchingly named config was found.
215              
216             - .config_check :: If it was told to check the configs or not for matching names.
217              
218             - .config_dir :: The config dir to check.
219              
220             - .config_ignored[] :: Array of configs ignored.
221              
222             - .config_ignore :: Configs asked to be ignored.
223              
224             - .exit :: Nagios style exit value.
225              
226             - .status.$name :: Status of each item.
227              
228             - .total :: Number of configured items.
229              
230             - .ignored[] :: A array of ignored configs.
231              
232             - .ignore :: A array of items asked to be ignored.
233              
234             - .config_dir_missing :: If the config dir is missing.
235              
236             - .config_dir_readable :: If the config dir is readable.
237              
238             - .status_list.$status :: A hash of the various statuses with keys being arrays of items for that status.
239              
240             - .results[] :: A descriptive a array of the results of the check.
241              
242             =cut
243              
244             sub run {
245 0     0 1   my $self = $_[0];
246              
247             my $to_return = {
248             configs => [],
249             config_not_running => [],
250             config_missing => [],
251             config_check => $self->{config_check},
252             config_dir => $self->{config_dir},
253             config_ignored => [],
254 0           config_ignore => [ sort( keys( %{ $self->{config_ignore} } ) ) ],
255             exit => 0,
256             status => {},
257             ignored => [],
258 0           ignore => [ sort( keys( %{ $self->{ignore} } ) ) ],
  0            
259             config_dir_missing => 0,
260             config_dir_readable => 1,
261             status_list => {
262             stopped => [],
263             starting => [],
264             running => [],
265             backoff => [],
266             stopping => [],
267             exited => [],
268             fatal => [],
269             unknown => [],
270             },
271             results => []
272             };
273              
274 0           my $output = `supervisorctl status 2> /dev/null`;
275 0           my @output_split = split( /\n/, $output );
276              
277 0           foreach my $line (@output_split) {
278 0           my ( $name, $status ) = split( /\s+/, $line );
279 0 0 0       if ( defined($status) && defined($name) ) {
280 0           $status = lc($status);
281 0 0         if ( defined( $self->{ignore}{$name} ) ) {
282 0           push( @{ $to_return->{ignored} }, $name );
  0            
283             } else {
284 0 0         if ( $self->{ignore}{$name} ) {
285 0           push( @{ $to_return->{ignored} }, $name );
  0            
286 0           push( @{ $to_return->{results} }, 'IGNORED - ' . $name . ', ' . $status );
  0            
287             } else {
288 0 0         if ( defined( $self->{status_mapping}{$status} ) ) {
289 0 0         if ( $to_return->{exit} < $self->{status_mapping}{$status} ) {
290 0           $to_return->{exit} = $self->{status_mapping}{$status};
291             }
292 0           $to_return->{status}{$name} = $status;
293 0           push( @{ $to_return->{status_list}{$status} }, $name );
  0            
294             push(
295 0           @{ $to_return->{results} },
296 0           $self->{val_to_string}{ $self->{status_mapping}{$status} } . ' - '
297             . $name . ', '
298             . $status
299             );
300             } ## end if ( defined( $self->{status_mapping}{$status...}))
301             } ## end else [ if ( $self->{ignore}{$name} ) ]
302             } ## end else [ if ( defined( $self->{ignore}{$name} ) ) ]
303             } ## end if ( defined($status) && defined($name) )
304             } ## end foreach my $line (@output_split)
305              
306             # check the config dir only if asked to
307 0 0         if ( $self->{config_check} ) {
308             # handling for if it does not exist
309 0 0         if ( -d $self->{config_dir} ) {
310 0           my @dir_entries;
311 0           eval { @dir_entries = read_dir( $self->{config_dir} ); };
  0            
312 0 0         if ($@) {
313 0           $to_return->{config_dir_readable} = 0;
314 0 0         if ( $to_return->{exit} < $self->{config_dir_nonreadable_val} ) {
315 0           $to_return->{exit} = $self->{config_dir_nonreadable_val};
316             }
317             }
318             # if it was readable, process it
319 0 0         if ( $to_return->{config_dir_readable} ) {
320             # a lookup hash of found configs
321 0           my %configs;
322             # process each dir entry
323 0           foreach my $entry ( sort(@dir_entries) ) {
324             # only process items ending in .conf and that are a file.
325 0 0 0       if ( $entry =~ /\.conf$/ && -f $self->{config_dir} . '/' . $entry ) {
326 0           $entry =~ s/\.conf$//;
327 0 0         if ( $self->{config_ignore}{$entry} ) {
328 0           push( @{ $to_return->{config_ignored} }, $entry );
  0            
329 0           push( @{ $to_return->{results} }, 'IGNORED - config ' . $entry );
  0            
330             } else {
331 0           push( @{ $to_return->{configs} }, $entry );
  0            
332 0           $configs{$entry} = 1;
333 0 0         if ( !defined( $to_return->{status}{$entry} ) ) {
334 0           push( @{ $to_return->{config_not_running} }, $entry );
  0            
335             push(
336 0           @{ $to_return->{results} },
337             $self->{val_to_string}{ $self->{not_running_val} }
338 0           . ' - non-running config "'
339             . $entry . '"'
340             );
341 0 0         if ( $to_return->{exit} < $self->{not_running_val} ) {
342 0           $to_return->{exit} = $self->{not_running_val};
343             }
344             } else {
345 0           push( @{ $to_return->{results} }, 'OK - config present for ' . $entry );
  0            
346             }
347             } ## end else [ if ( $self->{config_ignore}{$entry} ) ]
348              
349             } ## end if ( $entry =~ /\.conf$/ && -f $self->{config_dir...})
350             } ## end foreach my $entry ( sort(@dir_entries) )
351 0           foreach my $running ( keys( %{ $to_return->{status} } ) ) {
  0            
352             # only check if it is missing as we already check if a running item exists for a config previously
353 0 0         if ( !$configs{$running} ) {
354 0           push( @{ $to_return->{config_missing} }, $running );
  0            
355             push(
356 0           @{ $to_return->{results} },
357 0           $self->{val_to_string}{ $self->{not_running_val} } . ' - missing config ' . $running
358             );
359             }
360             } ## end foreach my $running ( keys( %{ $to_return->{status...}}))
361              
362             } ## end if ( $to_return->{config_dir_readable} )
363             } else {
364 0           $to_return->{config_dir_missing} = 1;
365 0 0         if ( $to_return->{exit} < $self->{config_dir_missing_val} ) {
366 0           $to_return->{exit} = $self->{config_dir_missing_val};
367             push(
368 0           @{ $to_return->{results} },
369             $self->{val_to_string}{ $self->{config_dir_missing_val} }
370             . ' - config dir,"'
371             . $self->{config_dir}
372 0           . '", missing'
373             );
374             } ## end if ( $to_return->{exit} < $self->{config_dir_missing_val...})
375             } ## end else [ if ( -d $self->{config_dir} ) ]
376             } ## end if ( $self->{config_check} )
377              
378 0           return $to_return;
379             } ## end sub run
380              
381             =pod
382              
383             =head1 AUTHOR
384              
385             Zane C. Bowers-Hadley, C<< >>
386              
387             =head1 BUGS
388              
389             Please report any bugs or feature requests to C, or through
390             the web interface at L. I will be notified, and then you'll
391             automatically be notified of progress on your bug as I make changes.
392              
393              
394              
395              
396             =head1 SUPPORT
397              
398             You can find documentation for this module with the perldoc command.
399              
400             perldoc Check::supervisorctl
401              
402              
403             You can also look for information at:
404              
405             =over 4
406              
407             =item * RT: CPAN's request tracker (report bugs here)
408              
409             L
410              
411             =item * Github issue tracker (report bugs here)
412              
413             L
414              
415             =item * CPAN Ratings
416              
417             L
418              
419             =item * Search CPAN
420              
421             L
422              
423             =back
424              
425              
426             =head1 ACKNOWLEDGEMENTS
427              
428              
429             =head1 LICENSE AND COPYRIGHT
430              
431             This software is Copyright (c) 2025 by Zane C. Bowers-Hadley.
432              
433             This is free software, licensed under:
434              
435             The GNU General Public License, Version 3, June 2007
436              
437              
438             =cut
439              
440             1; # End of Check::supervisorctl