Code

Added ability to output critical on error. Fixed open so it properly fails if the...
[nagiosplug.git] / contrib / check_sybase
1 #!/usr/bin/perl
2 # check_sybase
3 # A nagios plugin that connects to a Sybase database and checks free space.
4 #
5 # Copyright 2004-2005 Simon Bellwood, NetMan Network Management and IT 
6 #                                                               Services GmbH
7 # Portions Copyright 2001 Michael Peppler.
8 # License: GPL
9 #
10 # Bugs and feedback to simon.bellwood@NOSPAM.net-man.at
11 # Latest version available from:
12 #       http://www.net-man.at/software/check_sybase-LATEST.zip
13 #
14 # Revision history:
15 # 0.1   01-OCT-2004     Initial version.
16 # 0.2   08-NOV-2004     Initial release.
17 # 0.3   13-JAN-2005     Fixed lib path, improved timeouts.
18 # 0.4   26-JAN-2005     Added loginTimeout.
19 # 0.5   04-FEB-2005     Fixed dates in history. Oops.
20 # 0.6   29-MAR-2005     Added --explain option.
21 # 0.7   08-APR-2005     Added initial performance data support.
22 my $VERSION = "0.7";
24 #use warnings;
25 use strict;
26 use DBI;
27 use Getopt::Long;
28 use lib qw( /usr/lib/nagios/plugins/ /usr/local/nagios/libexec/ );
29 use utils qw(%ERRORS &print_revision &support &usage $TIMEOUT);
32 my $PROGNAME = "check_sybase";
33 my $DEFAULT_CHECKTYPE = "FREESPACE";
34 my $DEFAULT_WARNING   = "25";
35 my $DEFAULT_CRITICAL  = "10";
36 my $DEFAULT_TIMEOUT   = "30";
38 my ($user, $pass, $dbsvr, $dbname, $config, $checktype, $explain,
39     $warn, $crit, $timeout, $help, $version);
41 my $options_okay = GetOptions(
42         "U|user=s"      => \$user,
43         "P|pass:s"      => \$pass, # ":" means optional
44         "S|dbsvr=s"     => \$dbsvr,
45         "D|dbname=s"    => \$dbname,
46         "config=s"      => \$config,
47         "checktype=s"   => \$checktype,
48         "explain"       => \$explain,
49         "w|warning=i"   => \$warn,
50         "c|critical=i"  => \$crit,
51         "t|timeout=i"   => \$timeout,
52         "h|help"        => \$help,
53         "V|version"     => \$version
54 );
57 if (! $options_okay) # Bad option passed
58 {
59         &help;
60         &nunk("Bad command line option passed!");
61 }
63 # Use defaults, if needed
64 $warn = $warn || $DEFAULT_WARNING;
65 $crit = $crit || $DEFAULT_CRITICAL;
66 $checktype = $checktype || $DEFAULT_CHECKTYPE;
67 $timeout = $timeout || $TIMEOUT || $DEFAULT_TIMEOUT;
69 if ($help)
70 {
71         &help;
72         &nok;
73 }
75 if ($version)
76 {
77         print_revision($PROGNAME,"\$Revision$VERSION \$");
78         &nok;
79 }
81 if ($config) # Read any of "user", "pass", "dbsvr", "dbname" from config file
82 {
83         &read_config;
84 }
86 # Some more descriptive syntax checks
87 my $syntax_error;
88 $syntax_error .= "No dbsvr given! " unless $dbsvr;
89 $syntax_error .= "No dbname given! " unless $dbname;
90 $syntax_error .= "No user given! " unless $user;
91 $syntax_error .= "Bad checktype given!"
92         unless $checktype =~ m/^CONNECT|FREESPACE$/;
93 &nunk($syntax_error) if $syntax_error;
96 # Just in case of problems, let's not hang Nagios
97 $SIG{'ALRM'} = sub {
98         &nunk("Timeout: no response from dbsvr $dbsvr within $timeout seconds");
99 };
100 alarm($timeout);
103 # Decide on what we are checking
104 if ($checktype eq "CONNECT")
106         &connect;
108 elsif ($checktype eq "FREESPACE")
110         &check_space;
113 my $dbh;
114 my $is_connected;
115 sub connect
117         $dbh = DBI->connect("dbi:Sybase:server=$dbsvr;database=$dbname;".
118                        "timeout=$timeout,loginTimeout=$timeout", $user, $pass)
119                 or &ncrit("Could not connect to '$dbname' on '$dbsvr'");
121         # Report success for a check of type CONNECT
122         &nok("Connect okay") if $checktype ne "FREESPACE";
125 sub disconnect
127         $dbh->disconnect if $is_connected;
128         $is_connected = 0;
131 sub check_space
133         &connect;
135         # Most of this sub based on Michael Peppler's check-space.pl
136         # For debugging purposes, more values are collected than needed.
138         $dbh->{syb_do_proc_status} = 1;
140         my $dbinfo;
142         # First check space in the database
143         my $sth = $dbh->prepare("sp_spaceused")
144                 or &nunk("Failed to call sp_spaceused on '$dbsvr'");
145         $sth->execute
146                 or &nunk("Failed to call sp_spaceused on '$dbsvr'");
147         do {
148                 while(my $d = $sth->fetch)
149                 {
150                         if($d->[0] =~ /$dbname/)
151                         {
152                                 # Grab "database_size"
153                                 $d->[1] =~ s/[^\d.]//g;
154                                 $dbinfo->{size} = $d->[1];
155                         }
156                         else
157                         {
158                                 # Reserved, data, index, unused
159                                 foreach (@$d)
160                                 {
161                                         s/\D//g;
162                                 }
164                                 # Grab "reserved", "data", "index"
165                                 $dbinfo->{reserved} = $d->[0] / 1024;
166                                 $dbinfo->{data} = $d->[1] / 1024;
167                                 $dbinfo->{index} = $d->[2] / 1024;
168                                 $dbinfo->{unused} = $d->[3] / 1024;
169                         }
170                 }
171         } while($sth->{syb_more_results});
173         &explain("db size:  ".$dbinfo->{size});
174         &explain("reserved: ".$dbinfo->{reserved});
175         &explain(" data:    ".$dbinfo->{data});
176         &explain(" index:   ".$dbinfo->{index});
177         &explain(" unused:  ".$dbinfo->{unused});
179         # Get the actual device usage from sp_helpdb to get the free log space
180         $sth = $dbh->prepare("sp_helpdb $dbname")
181                 or &nunk("Failed to call sp_helpdb $dbname on '$dbsvr'");
182         $sth->execute
183                 or &nunk("Failed to call sp_helpdb $dbname on '$dbsvr'");
184         do {
185                 while(my $d = $sth->fetch)
186                 {
187                         # Look for "usage" column with value "log only"
188                         if($d->[2] && $d->[2] =~ /log only/)
189                         {
190                                 # Grab "size", add it to our log size
191                                 $d->[1] =~ s/[^\d\.]//g;
192                                 $dbinfo->{log} += $d->[1];
193                         }
195                         # Look for "device fragments" column with "log only"
196                         # followed by a number.
197                         if($d->[0] =~ /log only .* (\d+)/)
198                         {
199                                 $dbinfo->{logfree} = $1 / 1024;
200                         }
201                 }
202         } while($sth->{syb_more_results});
204         &explain("log: ".$dbinfo->{log});
205         &explain("logfree: ".$dbinfo->{logfree});
207         # Subtract the log size from the database size
208         $dbinfo->{realsize} = $dbinfo->{size} - $dbinfo->{log};
209         &explain("realsize (i.e. size - log) = ".$dbinfo->{realsize});
211         # The "reserved" space is free for use by the table that freed it, so 
212         # it is not truly free space. To be safe, our calculation ignores it.
213         my $free = ($dbinfo->{realsize} - $dbinfo->{reserved})/$dbinfo->{realsize};
214         $free = sprintf("%.2f", $free*100);
216         &explain("(realsize-reserved)/realsize = $free%");
217         &explain("For safety, this calculation assumes no log space reuse. ".
218         "Because of this, you may get negative values.");
221         if ($free < $crit)
222         {
223                 &ncrit("Free space is $free%! (critical threshold is $crit%)".
224                         "|free_space=$free%");
225         }
227         if ($free < $warn)
228         {
229                 &nwarn("Free space is $free%! (warning threshold is $warn%)".
230                         "|free_space=$free%");
231         }
234         &nok("Free space within thresholds ($free% free)".
235                 "|free_space=$free%");
238 sub read_config
240         open (CONFIG, "<$config")
241                 or &nunk("Failed to open config file '$config': $!");
242         while (<CONFIG>)
243         {
244                 chomp;
245                 next if m/^#/; # skip comments
246                 next if m/^$/; # skip blanks
248                 # Each case-insensitive argument can be followed by an optional
249                 # colon, then must be followed by whitespace and the value.
250                 # Options in the config file override those given on the 
251                 # command line, but don't rely on this!
253                 if    (m/USER:?\s+(\S+)/i)
254                 {
255                         $user = $1;
256                 }
257                 elsif (m/PASS:?\s+(\S+)/i)
258                 {
259                         $pass = $1;
260                 }
261                 elsif (m/DBSVR:?\s+(\S+)/i)
262                 {
263                         $dbsvr = $1;
264                 }
265                 elsif (m/DBNAME:?\s+(\S+)/i)
266                 {
267                         $dbname = $1;
268                 }
269                 else
270                 {
271                         &nunk("Invalid line $. in config file '$config'");
272                 }
273         }
274         close (CONFIG);
277 sub help
279         print <<_HELP_;
280 Usage: $PROGNAME OPTIONS
281 A nagios plugin that connects to a Sybase database and checks free space.
283 Mandatory arguments to long options are mandatory for short options too.
284   -U, --user            Username to connect to database.
285   -P, --pass            Password to connect to database.
286   -S, --dbsvr           Database server (as in the interfaces file).
287   -D, --dbname          Database name to check.
288   --config=FILE         Config file (see SECURITY below)
289   --checktype=TYPE      Type of check to run (see TYPEs below)
290   --explain             Explains how we calculated the free space.
291   -w, --warning         Warning threshold, in percent (default 25)
292   -c, --critical        Critical threshold, in percent (default 10)
293   -t, --timeout         Timeout value, in seconds (default 30)
294   -h, --help            This help message
295   -V, --version         Version information ($VERSION)
297 Examples:
298         $PROGNAME -U sa -P secret -S bigbox -D orders
299         $PROGNAME --config=/secure/nagios-sybase.cfg --checktype=CONNECT
301 TYPEs
302  There are two types of checks you can run:
303  --checktype=CONNECT
304     Checks just the connection to the database.
305  --checktype=FREESPACE
306     (Default) Checks both the connection to the database and the free space.
308 SECURITY - Using a config file
309  Since a "ps ax" will reveal your database username and password, you can 
310  instead specify them in a config file. Pass the config file with --config.
311  The format of the file is:
312    USER     value
313    PASS     value
314  You can also specify a DBSVR and DBNAME in the file. Comments (#) and blank
315  lines are ignored. Use whitespace to separate argument and value.
316 _HELP_
320 sub explain
322         return unless $explain;
324         my $msg = shift;
325         print "$msg\n";
330 # Some wrappers..
332 # Returns code 0, OK
333 sub nok
335         my $msg = shift;
336         print "OK: $msg\n" if $msg;
338         &disconnect;
339         exit $ERRORS{OK};
342 # Returns code 1, Warning
343 sub nwarn
345         my $msg = shift;
346         print "WARNING: $msg\n";
348         &disconnect;
349         exit $ERRORS{WARNING};
352 # Returns code 2, Critical
353 sub ncrit
355         my $msg = shift;
356         print "CRITICAL: $msg\n";
358         &disconnect;
359         exit $ERRORS{CRITICAL};
362 # Returns code 3, Unknown
363 sub nunk
365         my $msg = shift;
366         print "ERROR: $msg\n";
368         &disconnect;
369         exit $ERRORS{UNKNOWN};