]> git.evergreen-ils.org Git - OpenSRF.git/blob - examples/nagios/check_osrf_services
bump up version numbers for 3.1.0-beta release
[OpenSRF.git] / examples / nagios / check_osrf_services
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5 use Getopt::Long;
6 use OpenSRF::System;
7 use OpenSRF::AppSession;
8 use OpenSRF::EX qw(:try);
9
10 # Sane-ish default
11 my $opt_osrf_config = '/openils/conf/opensrf_core.xml';
12
13 # For storing the list of supposedly active services
14 my @services;
15 # For storing our list of routers to check
16 my @routers;
17
18 GetOptions(
19     'osrf-config=s' => \$opt_osrf_config,
20 );
21
22 # If we can't bootstrap then something is horribly wrong!
23 # Probably "ejabberd isn't running"
24 try {
25     OpenSRF::System->bootstrap_client(config_file => $opt_osrf_config);
26 } otherwise {
27     print "Bootstrap failed\n";
28     exit 2;
29 };
30
31 # This gets the list of supposedly active services
32 sub prep_service_list {
33     # Using settings directly, as I don't know how to ask with pre-existing classes
34     my $session = OpenSRF::AppSession->create('opensrf.settings');
35     try {
36         $session->connect;
37     } otherwise {
38         print "Settings Connect Failed\n";
39         exit 2;
40     };
41     # This xpath is "Find every instace of an appname node under an activeapps node, anywhere"
42     # It should grab every app configured to run on any drone
43     # If your config contains apps that are not run on real drones you will get errors ;)
44     my $req = $session->request('opensrf.settings.xpath.get', '//activeapps/appname');
45     my $list = $req->recv;
46
47     if(UNIVERSAL::isa($list,"Error")) {
48         print "Active Apps List Failed\n";
49         exit 2;
50     }
51
52     $req->finish;
53     # Quick and dirty de-dupe
54     my %u_list = map { ($_ => 1) } @{$list->content};
55     # And save for later
56     @services = keys(%u_list);
57
58     $session->finish;
59     $session->disconnect;
60 }
61
62 # This gets the list of supposedly active routers
63 # This relies on the bootstrap being accurate in that regard
64 sub prep_routers_list {
65     # First, we grab our (hopefully) cached config
66     my $config = OpenSRF::Utils::Config->current;
67     # Loop over it quick
68     foreach(@{$config->bootstrap->routers}) {
69         # And make entries for each router
70         my $router = {};
71         $router->{name} = $_->{name};
72         $router->{domain} = $_->{domain};
73         # If we don't have a services list assume all active ones (aka, private router)
74         $router->{services} = \@services unless $_->{services};
75         # Otherwise, make note of what we are supposed to be running (aka, public router)
76         $router->{services} = $_->{services}->{service} if $_->{services};
77         # And tack it onto the list
78         push @routers, $router;
79     }
80 }
81
82 # This does the actual checking of routers/services
83 sub check_routers {
84     # Shortcut
85     my $conf = OpenSRF::Utils::Config->current;
86     foreach my $router (@routers) {
87         # HACK WARNING - This changes the router we will be querying
88         # This basically edits the cached bootstrap file. This is not guaranteed to keep working.
89         # This does NOT change what domain we are querying from
90         $conf->bootstrap->router_name($router->{name});
91         $conf->bootstrap->domain($router->{domain});
92         # Assume things failed unless they didn't.
93         my $failed = 1;
94         # First, check the router to see what it claims to have active services-wise
95         my $session = OpenSRF::AppSession->create('router');
96         try {
97             $failed = 0 if $session->connect;
98         } otherwise {
99             $failed = 1;
100         };
101         if($session->state != $session->CONNECTED || $failed) {
102             $router->{online} = 0;
103             next;
104         }
105         # Yay router commands! This should give us all services with at least one listener
106         my $req = $session->request('opensrf.router.info.class.list');
107         my $class_list = $req->recv;
108         $req->finish;
109
110         if(UNIVERSAL::isa($class_list,"Error")) {
111             $session->finish;
112             $session->disconnect;
113             $router->{online} = 0;
114             next;
115         }
116
117         # If we got an answer then this router is online!
118         $router->{online} = 1;
119         # Counters and storage for services checks
120         $router->{checked} = 0;
121         $router->{pass} = 0;
122         $router->{failed} = [];
123         # Quick reference of what the router told us it has
124         my %online_services = map { ($_ => 1) } @{$class_list->content};
125         foreach my $service (@{$router->{services}}) {
126             # This skips services not in the active list. Mainly for routers with explicit lists (aka, public routers) that not all may be configured to run.
127             next unless grep { $service eq $_ } @services;
128             # Assume we did not pass until proven otherwise
129             my $passed = 0;
130             $router->{checked} += 1;
131             if($online_services{$service}) {
132                 # Check the service, even if a listener is registered it may be dead
133                 my $session2 = OpenSRF::AppSession->create($service);
134                 try {
135                     $session2->connect;
136                 };
137                 if($session2->state == $session2->CONNECTED) {
138                     # To my knowledge, EVERY service should have atomic echo available
139                     my $req2 = $session2->request('opensrf.system.echo.atomic','Test');
140                     my $testresult = $req2->recv;
141                     if(!UNIVERSAL::isa($testresult,"Error")) {
142                         # If we got back what we passed in the service is working! Ish. Not a flawless test.
143                         $passed = 1 if @{$testresult->content}[0] eq 'Test';
144                     }
145                     $req2->finish;
146                     $session2->finish;
147                     $session2->disconnect;
148                 }
149             }
150             if($passed) {
151                 # Looks like it works, make note!
152                 $router->{pass} += 1;
153             } else {
154                 # Doesn't work! Save for later reporting.
155                 push @{$router->{failed}}, $service;
156             }
157         }
158         $session->finish;
159         $session->disconnect;        
160     }
161 }
162
163 # This outputs the result for Nagios
164 sub output_result {
165     # Counters/storage
166     my $checked_services = 0;
167     my $up_services = 0;
168     my @down_services;
169     my @down_routers;
170     # Assume all is good until proven otherwise
171     my $retcode = 0;
172     foreach my $router (@routers) {
173         # If the router isn't online then we don't need to look at services - We didn't check any!
174         if(!$router->{online}) {
175             push @down_routers, $router->{domain};
176             next;
177         }
178         # Otherwise increment our counters as needed
179         $checked_services += $router->{checked};
180         $up_services += $router->{pass};
181         foreach (@{$router->{failed}}) {
182             # Keep track of any down services for reporting in a minute
183             push @down_services, $router->{domain} . ':' . $_;
184         }
185     }
186     if(@down_routers) {
187         # Down routers are really bad. Chances are there will only ever be one here (public), but join with commas anyway.
188         print "Router(s) Offline: " . join(', ', @down_routers) . "\n";
189         $retcode = 2;
190     } elsif ($checked_services != $up_services) {
191         # Non-responsive services are also really bad
192         print "Service(s) not responding\n";
193         $retcode = 2;
194     } else {
195         # But if we have nothing then things are good!
196         print "Routers/Services OK\n";
197     }
198     # If there are down services then spit them out as additional information.
199     print "$_\n" foreach (@down_services);
200     # And return our response code
201     exit $retcode;
202 }
203
204 # CHEAT - We need SettingsClient to have cached stuff
205 try {
206     OpenSRF::Utils::SettingsClient->new()->config_value('none');
207 } otherwise {
208     print "Settings Fetch Failed\n";
209     exit 2;
210 };
211 # And run all of the above functions
212 prep_service_list();
213 prep_routers_list();
214 check_routers();
215 output_result();
216
217 # This code should NEVER run, as the only way out of output_result is an exit statement
218 print "What? I shouldn't have reached here.";
219 exit 3;