1 |
msalle |
2457 |
#!/usr/bin/perl |
2 |
|
|
# |
3 |
|
|
# Copyright (C) Nikhef 2011 |
4 |
|
|
# |
5 |
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
6 |
|
|
# you may not use this file except in compliance with the License. |
7 |
|
|
# You may obtain a copy of the License at |
8 |
|
|
# |
9 |
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
10 |
|
|
# |
11 |
|
|
# Unless required by applicable law or agreed to in writing, software |
12 |
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
13 |
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 |
|
|
# See the License for the specific language governing permissions and |
15 |
|
|
# limitations under the License. |
16 |
|
|
# |
17 |
|
|
# Author: |
18 |
|
|
# Mischa Sall\'e <msalle@nikhef.nl> |
19 |
|
|
# NIKHEF Amsterdam, the Netherlands |
20 |
|
|
# |
21 |
|
|
######################################################################## |
22 |
|
|
# |
23 |
|
|
# Nagios probe to test functioning of EES |
24 |
|
|
# |
25 |
|
|
# Nagios state can be one of the following: |
26 |
|
|
# |
27 |
|
|
######################################################################## |
28 |
|
|
|
29 |
|
|
# DEFAULTS |
30 |
|
|
my $probeversion=0.1; |
31 |
|
|
|
32 |
|
|
# Note the following defaults can be overridden using cmdline options |
33 |
|
|
my $deftimeout=10; # Overall timeout for probe |
34 |
|
|
my $defwarning=5; # When to warn about slow running |
35 |
|
|
my $defhost="localhost"; |
36 |
|
|
my $defport=6217; |
37 |
|
|
|
38 |
|
|
######################################################################## |
39 |
|
|
# Logging package |
40 |
|
|
# keeps internal log trace which can be dumped with dump_log |
41 |
|
|
######################################################################## |
42 |
|
|
package logger; |
43 |
|
|
use strict; |
44 |
|
|
use warnings; |
45 |
|
|
{ |
46 |
|
|
my $loglevel; |
47 |
|
|
my @logstring; |
48 |
|
|
|
49 |
|
|
# Constructor |
50 |
|
|
sub new { |
51 |
|
|
my $classname=shift; |
52 |
|
|
my $self={}; bless $self; |
53 |
|
|
my $level=shift; |
54 |
|
|
if (defined $level) { |
55 |
|
|
$self->set_loglevel($level); |
56 |
|
|
} else { |
57 |
|
|
$loglevel=0; |
58 |
|
|
} |
59 |
|
|
return $self; |
60 |
|
|
} |
61 |
|
|
|
62 |
|
|
# Sets loglevel |
63 |
|
|
sub set_loglevel($) { |
64 |
|
|
my $self=shift; |
65 |
|
|
my $level=shift; |
66 |
|
|
$loglevel=$level; |
67 |
|
|
} |
68 |
|
|
|
69 |
|
|
# Logging function: log_func(priority, "logstring\n"); |
70 |
|
|
sub log_func($@) { |
71 |
|
|
my $self=shift; |
72 |
|
|
my $prio=shift; |
73 |
|
|
return if ($prio > $loglevel); |
74 |
|
|
for my $line (@_) { |
75 |
|
|
push @logstring,$line; |
76 |
|
|
} |
77 |
|
|
} |
78 |
|
|
|
79 |
|
|
# Dumps log |
80 |
|
|
sub get_log(@) { |
81 |
|
|
my $self=shift; |
82 |
|
|
foreach my $myentry ( @logstring ) { |
83 |
|
|
print $myentry; |
84 |
|
|
} |
85 |
|
|
} |
86 |
|
|
} |
87 |
|
|
|
88 |
|
|
######################################################################## |
89 |
|
|
# Nagios status printing package |
90 |
|
|
# Can set and dump nagios status output |
91 |
|
|
######################################################################## |
92 |
|
|
package nagstat; |
93 |
|
|
{ |
94 |
|
|
my $code; |
95 |
|
|
my $summary; |
96 |
|
|
my $perfdata; |
97 |
|
|
my @stat; |
98 |
|
|
|
99 |
|
|
# Constructor |
100 |
|
|
sub new() { |
101 |
|
|
my $classname=shift; |
102 |
|
|
my $self={}; bless $self; |
103 |
|
|
$code=3; # Default status unknown |
104 |
|
|
$summary=undef; |
105 |
|
|
$perfdata=undef; |
106 |
|
|
@stat=("OK","WARNING","CRITICAL","UNKNOWN"); |
107 |
|
|
return $self; |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
# Set nagios code (0-3) plus summary |
111 |
|
|
sub set_status($$) { |
112 |
|
|
my $self=shift; |
113 |
|
|
if (!defined $summary) { |
114 |
|
|
$code=shift; |
115 |
|
|
$summary=shift; |
116 |
|
|
} |
117 |
|
|
} |
118 |
|
|
|
119 |
|
|
# Set internal performance data |
120 |
|
|
sub set_perfdata($) { |
121 |
|
|
my $self=shift; |
122 |
|
|
$perfdata=shift; |
123 |
|
|
} |
124 |
|
|
|
125 |
|
|
# Printout nagios status, summary and optionally performance data |
126 |
|
|
# return value is code (0-3) |
127 |
|
|
sub get_status { |
128 |
|
|
if (!defined $summary) { |
129 |
|
|
$summary="unknown status"; |
130 |
|
|
} |
131 |
|
|
if (defined $perfdata) { |
132 |
|
|
print $stat[$code].": ".$summary."|".$perfdata."\n"; |
133 |
|
|
} else { |
134 |
|
|
print $stat[$code].": ".$summary."\n"; |
135 |
|
|
} |
136 |
|
|
return $code; |
137 |
|
|
} |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
######################################################################## |
141 |
|
|
# Inter process communication package for nagios probes |
142 |
|
|
# Starts alarm handler when receiving alarm which checks status of |
143 |
|
|
# probe, and terminates or kills it. |
144 |
|
|
######################################################################## |
145 |
|
|
package probeipc; |
146 |
|
|
{ |
147 |
|
|
my $timeout; |
148 |
|
|
|
149 |
|
|
# Constructor: new(exitfunc,[kill time], [term time]) |
150 |
|
|
sub new() { |
151 |
|
|
my $classname=shift; |
152 |
|
|
my $self={}; bless $self; |
153 |
|
|
my $alarmhandler=shift |
154 |
|
|
or die ($classname."::new() needs alarmhandler arg\n"); |
155 |
|
|
my $inthandler=shift |
156 |
|
|
or die ($classname."::new() needs inthandler arg\n"); |
157 |
|
|
my $timeout=(shift or 10); # probe default timeout is 10 |
158 |
|
|
$self->set_alarmhandler($alarmhandler); |
159 |
|
|
$self->set_inthandler($inthandler); |
160 |
|
|
$self->set_timeout($timeout); |
161 |
|
|
return $self; |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
# Sets time after which to send SIGKILL |
165 |
|
|
sub set_timeout($) { |
166 |
|
|
my $self=shift; |
167 |
|
|
$timeout=shift; |
168 |
|
|
} |
169 |
|
|
|
170 |
|
|
# Sets function to call when SIGALRM is caught |
171 |
|
|
sub set_alarmhandler($) { |
172 |
|
|
my $self=shift; |
173 |
|
|
my $alarmhandler=shift; |
174 |
|
|
# \& for function reference, $ for stringvar |
175 |
|
|
$SIG{'ALRM'} = \&$alarmhandler; |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
# Sets function to call when SIGINT or SIGTERM is caught |
179 |
|
|
sub set_inthandler($) { |
180 |
|
|
my $self=shift; |
181 |
|
|
my $inthandler=shift; |
182 |
|
|
$SIG{'INT'} = \&$inthandler; |
183 |
|
|
$SIG{'TERM'} = \&$inthandler; |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
} |
187 |
|
|
|
188 |
|
|
######################################################################## |
189 |
|
|
# Running main probe package |
190 |
|
|
######################################################################## |
191 |
|
|
package main; |
192 |
|
|
use strict; |
193 |
|
|
use warnings; |
194 |
|
|
|
195 |
|
|
use IO::Socket; |
196 |
|
|
use Getopt::Long qw(:config no_ignore_case bundling); |
197 |
|
|
use Time::HiRes qw(time alarm); |
198 |
|
|
|
199 |
|
|
my $timeout; # Total maximum runtime for probe |
200 |
|
|
my $critical; # Time after which to kill gLExec |
201 |
|
|
my $warning; # Time after which to warn about slow gLExec |
202 |
|
|
my $host; # EES hostname |
203 |
|
|
my $port; # EES portnumber |
204 |
|
|
my $verbose; # Verbosity level |
205 |
|
|
|
206 |
|
|
my $sock; # socket to EES |
207 |
|
|
# Define different stages, such that e.g. the sighandlers know where we are |
208 |
|
|
my %stages=( |
209 |
|
|
'presock' => 0, |
210 |
|
|
'sockopen' => 1, |
211 |
|
|
'datasent' => 2, |
212 |
|
|
'headrcvd' => 3, |
213 |
|
|
'resprcvd' => 4, |
214 |
|
|
'sockclosed'=> 5 |
215 |
|
|
); |
216 |
|
|
my $stage=$stages{'presock'}; # Which state the socket is in |
217 |
|
|
|
218 |
|
|
# Prints usage output |
219 |
|
|
sub usage() { |
220 |
|
|
(my $name = $0) =~ s/.*\///; |
221 |
|
|
print <<EOHELP; |
222 |
|
|
Usage: $name [options] |
223 |
|
|
|
224 |
|
|
Options: |
225 |
|
|
-t|--timeout <timeout> maximum runtime for probe, default: $deftimeout sec |
226 |
|
|
-c|--critical <timeout> idem |
227 |
|
|
-w|--warning <time> runtime after which to warn, default: $defwarning sec |
228 |
|
|
-H|--host <hostname> hostname |
229 |
|
|
-p|--port <portnumber> port number |
230 |
|
|
-v|--verbose be more verbose, more -v means more verbosity |
231 |
|
|
-V|--version print version |
232 |
|
|
-h|--help show this helptext |
233 |
|
|
EOHELP |
234 |
|
|
exit 0; |
235 |
|
|
} |
236 |
|
|
|
237 |
|
|
# Prints short usage output (oneline) |
238 |
|
|
sub shortusage() { |
239 |
|
|
(my $name = $0) =~ s/.*\///; |
240 |
|
|
print <<EOHELP; |
241 |
|
|
Usage: $name [options] |
242 |
|
|
EOHELP |
243 |
|
|
} |
244 |
|
|
|
245 |
|
|
# Prints probe version |
246 |
|
|
sub version() { |
247 |
|
|
(my $name = $0) =~ s/.*\///; |
248 |
|
|
print <<EOHELP; |
249 |
|
|
$name version: $probeversion |
250 |
|
|
EOHELP |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
# Parses command line options and sets global variables |
254 |
|
|
sub getopts() { |
255 |
|
|
my $version; |
256 |
|
|
my $help; |
257 |
|
|
my $shorthelp; |
258 |
|
|
|
259 |
|
|
$timeout=$deftimeout; |
260 |
|
|
$warning=$defwarning; |
261 |
|
|
$host=$defhost; |
262 |
|
|
$port=$defport; |
263 |
|
|
GetOptions( |
264 |
|
|
"t|timeout=i" => \$timeout, |
265 |
|
|
"c|critical=i" => \$timeout, |
266 |
|
|
"w|warning=i" => \$warning, |
267 |
|
|
"H|host=s" => \$host, |
268 |
|
|
"p|port=i" => \$port, |
269 |
|
|
"u|url=s", |
270 |
|
|
"v|verbose+" => \$verbose, |
271 |
|
|
"help+" => \$help, |
272 |
|
|
"h+" => \$shorthelp, |
273 |
|
|
"V|version+" => \$version) or &usage and exit(1); |
274 |
|
|
|
275 |
|
|
$help and &usage and exit(0); |
276 |
|
|
$shorthelp and &shortusage and exit(0); |
277 |
|
|
$version and &version and exit(0); |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
# Exit function: prints nagios status and dumps log |
281 |
|
|
sub nagios_exit() { |
282 |
|
|
my $rc=nagstat->get_status(); |
283 |
|
|
|
284 |
|
|
# Logging object |
285 |
|
|
logger->get_log(); |
286 |
|
|
|
287 |
|
|
exit $rc; |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
# Signal handler for SIGALRM |
291 |
|
|
sub alarm_handler() { |
292 |
|
|
my ($sig)=@_; |
293 |
|
|
logger->log_func(2,"Timeout exceeded\n"); |
294 |
|
|
if ($stage>$stages{'presock'} && $stage<$stages{'sockclosed'}) { |
295 |
|
|
logger->log_func(2,"Socket has been opened, closing it\n"); |
296 |
|
|
close ($sock); |
297 |
|
|
} |
298 |
|
|
nagstat->set_status(2,"probe timeout exceeded"); |
299 |
|
|
nagios_exit; |
300 |
|
|
} |
301 |
|
|
|
302 |
|
|
# Signal handler for SIGINT and SIGTERM |
303 |
|
|
sub int_handler() { |
304 |
|
|
my ($sig)=@_; |
305 |
|
|
logger->log_func(2,"Caught signal ".$sig."\n"); |
306 |
|
|
if ($stage>$stages{'presock'} && $stage<$stages{'sockclosed'}) { |
307 |
|
|
logger->log_func(2,"Socket has been opened, closing it\n"); |
308 |
|
|
close ($sock); |
309 |
|
|
} |
310 |
|
|
nagstat->set_status(2,"caught signal ".$sig); |
311 |
|
|
nagios_exit; |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
# Actual probe opening socket to EES, sending message, receiving response and |
315 |
|
|
# parsing the result |
316 |
|
|
sub call_ees($) { |
317 |
|
|
my $msg=shift; |
318 |
|
|
my $t1; |
319 |
|
|
my $t2; |
320 |
|
|
|
321 |
|
|
# Make sure to have starttime |
322 |
|
|
$t1=time(); |
323 |
|
|
|
324 |
|
|
# Set alarm |
325 |
|
|
alarm($timeout); |
326 |
|
|
|
327 |
|
|
logger->log_func(2,"Opening connection to ".$host.":".$port."\n"); |
328 |
|
|
# Open socket to $host:$port |
329 |
|
|
$sock = IO::Socket::INET->new( |
330 |
|
|
PeerAddr => $host, |
331 |
|
|
PeerPort => $port, |
332 |
|
|
Proto => 'tcp' |
333 |
|
|
); |
334 |
|
|
if (!defined $sock) { |
335 |
|
|
nagstat->set_status(2,"Failed to connect ($!)"); |
336 |
|
|
return 1; |
337 |
|
|
} |
338 |
|
|
$stage=$stages{'sockopen'}; |
339 |
|
|
|
340 |
|
|
# Send soap message |
341 |
|
|
logger->log_func(3,"Socket opened, sending message\n"); |
342 |
|
|
print $sock $msg; |
343 |
|
|
$stage=$stages{'datasent'}; |
344 |
|
|
|
345 |
|
|
# Get header and response |
346 |
|
|
logger->log_func(3,"Message sent, waiting for response\n"); |
347 |
|
|
my $header = <$sock>; |
348 |
|
|
if (!defined $header) { |
349 |
|
|
my $summary="cannot read from socket ($!)"; |
350 |
|
|
close($sock); |
351 |
|
|
nagstat->set_status(2,$summary); |
352 |
|
|
return 1; |
353 |
|
|
} |
354 |
|
|
$stage=$stages{'headrcvd'}; |
355 |
|
|
|
356 |
|
|
# Chop of any carriage-return or line-feed from header |
357 |
|
|
$header =~ s/[\r\n]+$//; |
358 |
|
|
logger->log_func(3,"Header ".$header." received\n"); |
359 |
|
|
|
360 |
|
|
# Dump remaining response in log |
361 |
|
|
logger->log_func(3,"Reading remaining response\n"); |
362 |
|
|
while(my $line=<$sock>) { |
363 |
|
|
logger->log_func(3,"$line"); |
364 |
|
|
} |
365 |
|
|
logger->log_func(3,"\n"); |
366 |
|
|
$stage=$stages{'resprcvd'}; |
367 |
|
|
|
368 |
|
|
# Close socket |
369 |
|
|
logger->log_func(3,"Response finished, closing socket\n"); |
370 |
|
|
close($sock); |
371 |
|
|
$stage=$stages{'sockclosed'}; |
372 |
|
|
logger->log_func(3,"Socket closed\n"); |
373 |
|
|
# We are done with the socket, we have timing statistics |
374 |
|
|
$t2=time(); |
375 |
|
|
|
376 |
|
|
# Set performance data |
377 |
|
|
my $dt=int(($t2-$t1)*1000+0.5)/1000; |
378 |
|
|
nagstat->set_perfdata("${dt}s;$warning;$timeout;0"); |
379 |
|
|
|
380 |
|
|
# Check header |
381 |
|
|
if ("$header" eq "HTTP/1.1 200 OK") { |
382 |
|
|
if ($dt<$warning) { |
383 |
|
|
nagstat->set_status(0,"Success"); |
384 |
|
|
return 0; |
385 |
|
|
} |
386 |
|
|
nagstat->set_status(1,"EES is slow in responding"); |
387 |
|
|
return 0; |
388 |
|
|
} |
389 |
|
|
# There was a problem, chop of HTTP/1.1 and set status |
390 |
|
|
if ("$header" =~ /HTTP\/1.1 .*/) { |
391 |
|
|
nagstat->set_status(1,"unexpected answer from host ($header)"); |
392 |
|
|
} else { |
393 |
|
|
nagstat->set_status(2,"not a valid response ($header)"); |
394 |
|
|
} |
395 |
|
|
return 1; |
396 |
|
|
} |
397 |
|
|
|
398 |
|
|
my $msg= <<EOF; |
399 |
|
|
<?xml version="1.0" encoding="UTF-8"?> |
400 |
|
|
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" |
401 |
|
|
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" |
402 |
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
403 |
|
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
404 |
|
|
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" |
405 |
|
|
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" |
406 |
|
|
xmlns:XACMLcontext="urn:oasis:names:tc:xacml:2.0:context:schema:os" |
407 |
|
|
xmlns:XACMLassertion="urn:oasis:names:tc:xacml:2.0:profile:saml2.0:v2:schema:assertion" |
408 |
|
|
xmlns:XACMLpolicy="urn:oasis:names:tc:xacml:2.0:policy:schema:os" |
409 |
|
|
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" |
410 |
|
|
xmlns:XACMLService="http://www.globus.org/security/XACMLAuthorization/bindings" |
411 |
|
|
xmlns:XACMLsamlp="urn:oasis:names:tc:xacml:2.0:profile:saml2.0:v2:schema:protocol" |
412 |
|
|
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"> |
413 |
|
|
<SOAP-ENV:Body> |
414 |
|
|
<XACMLsamlp:XACMLAuthzDecisionQuery CombinePolicies="true" ReturnContext="true" |
415 |
|
|
InputContextOnly="false" IssueInstant="2010-03-25T14:55:01Z" Version="2.0" |
416 |
|
|
ID="ID-1804289383"> |
417 |
|
|
<saml:Issuer xsi:type="saml:NameIDType" |
418 |
|
|
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName">NetCat</saml:Issuer> |
419 |
|
|
<XACMLcontext:Request xsi:type="XACMLcontext:RequestType"> |
420 |
|
|
<XACMLcontext:Action xsi:type="XACMLcontext:ActionType"> |
421 |
|
|
</XACMLcontext:Action> |
422 |
|
|
</XACMLcontext:Request> |
423 |
|
|
</XACMLsamlp:XACMLAuthzDecisionQuery> |
424 |
|
|
</SOAP-ENV:Body> |
425 |
|
|
</SOAP-ENV:Envelope> |
426 |
|
|
EOF |
427 |
|
|
|
428 |
|
|
# Parse commandline options |
429 |
|
|
getopts(); |
430 |
|
|
|
431 |
|
|
# Initialize logger and set loglevel |
432 |
|
|
logger->new($verbose); |
433 |
|
|
|
434 |
|
|
# Initialize objects |
435 |
|
|
nagstat->new(); |
436 |
|
|
|
437 |
|
|
# Initialize signal handling |
438 |
|
|
probeipc->new(\&alarm_handler,\&int_handler,$timeout); |
439 |
|
|
|
440 |
|
|
# run actual EES probe |
441 |
|
|
call_ees($msg); |
442 |
|
|
|
443 |
|
|
# Dump nagios status, log and exit |
444 |
|
|
nagios_exit(); |
445 |
|
|
|