From c6e8fa8ad3bc00bb42632344e97823d8398dc3cd Mon Sep 17 00:00:00 2001 From: "Bjarni R. Einarsson" Date: Tue, 27 Sep 2011 15:16:36 +0000 Subject: [PATCH] First draft of code to handle arbitrary table data --- .../Nagios/DBD/MSSQL/Server.pm | 171 ++++++++++-------- .../plugins-scripts/check_mssql_health.pl | 38 +++- 2 files changed, 128 insertions(+), 81 deletions(-) diff --git a/check_mssql_health/src/check_mssql_health-1.5.8.2/plugins-scripts/Nagios/DBD/MSSQL/Server.pm b/check_mssql_health/src/check_mssql_health-1.5.8.2/plugins-scripts/Nagios/DBD/MSSQL/Server.pm index 7640acf..a269d37 100644 --- a/check_mssql_health/src/check_mssql_health-1.5.8.2/plugins-scripts/Nagios/DBD/MSSQL/Server.pm +++ b/check_mssql_health/src/check_mssql_health-1.5.8.2/plugins-scripts/Nagios/DBD/MSSQL/Server.pm @@ -224,30 +224,57 @@ sub init { } } elsif ($params{mode} =~ /^server::sql/) { $self->set_local_db_thresholds(%params); - if ($params{regexp}) { - # sql output is treated as text - if ($params{name2} eq $params{name}) { - $self->add_nagios_unknown(sprintf "where's the regexp????"); - } else { - $self->{genericsql} = - $self->{handle}->fetchrow_array($params{selectname}); - if (! defined $self->{genericsql}) { - $self->add_nagios_unknown(sprintf "got no valid response for %s", - $params{selectname}); - } + + if ($params{regexp} and ($params{name2} eq $params{name})) { + $self->add_nagios_unknown(sprintf "where's the regexp????"); + } + else { + my $data; + if ($params{sqlname}) { + $data = [$self->{handle}->fetchall_array($params{selectname})]; } - } else { - # sql output must be a number (or array of numbers) - @{$self->{genericsql}} = - $self->{handle}->fetchrow_array($params{selectname}); - if (! (defined $self->{genericsql} && - (scalar(grep { /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/ } @{$self->{genericsql}})) == - scalar(@{$self->{genericsql}}))) { + else { + # Backwards compatibility: only fetch one row if we don't + # have a naming column defined. + $data = [[$self->{handle}->fetchrow_array($params{selectname})]]; + } + my $rows = scalar(@{ $data }); + my $cols = scalar(@{ ($rows ? $data->[0] : []) }); + if ((0 == $rows) or (0 == $cols)) { + # Invalid: No rows, or first row has no columns. $self->add_nagios_unknown(sprintf "got no valid response for %s", - $params{selectname}); - } else { - # name2 in array - # units in array + $params{selectname}); + } + elsif (($params{sqlname} and $params{sqlname} > $cols) or + ($params{sqlinfo} and $params{sqlinfo} > $cols) or + ($params{sqleval} and $params{sqleval} > $cols)) { + # Invalid: sqlname, sqlinfo or sqleval column is out of bounds + $self->add_nagios_unknown(sprintf "got too few columns for %s", + $params{selectname}); + } + elsif ($params{regexp}) { + $self->{genericsql} = $data; + # No particular conditions... + } + else { + # Scan through the data and make sure it is all either numeric or + # one of our magic columns. + my $problems = 0; + foreach my $row (@{ $data }) { + for (my $i = 1; $i <= $cols; $i++) { + if ($params{sqlname} and ($i == $params{sqlname})) { next; } + if ($params{sqlinfo} and ($i == $params{sqlinfo})) { next; } + if ($row->[$i-1] !~ /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/) { + $problems += 1; + } + } + } + if ($problems) { + $self->add_nagios_unknown(sprintf "got no valid response for %s", + $params{selectname}); + } else { + $self->{genericsql} = $data; + } } } } elsif ($params{mode} =~ /^my::([^:.]+)/) { @@ -297,6 +324,7 @@ sub init { } else { printf "broken mode %s\n", $params{mode}; } + } sub dump { @@ -410,53 +438,56 @@ sub nagios { $self->{connectedusers}, $self->{warningrange}, $self->{criticalrange}); } elsif ($params{mode} =~ /^server::sql/) { - if ($params{regexp}) { - if (substr($params{name2}, 0, 1) eq '!') { - $params{name2} =~ s/^!//; - if ($self->{genericsql} !~ /$params{name2}/) { - $self->add_nagios_ok( - sprintf "output %s does not match pattern %s", - $self->{genericsql}, $params{name2}); - } else { - $self->add_nagios_critical( - sprintf "output %s matches pattern %s", - $self->{genericsql}, $params{name2}); + + foreach my $row (@{ $self->{genericsql} }) { + # the item in the list will be checked, or the first. + my $evalidx = $params{sqleval} ? ($params{sqleval}-1) : 0; + my $data = $row->[$evalidx]; + + if ($params{regexp}) { + my $test = $params{name2}; + my $true = !($test =~ s/^!//); + if ($true == ($data =~ $test)) { + $self->add_nagios_ok(sprintf "output %s matches pattern /%s/", + $data, $params{name2}); } - } else { - if ($self->{genericsql} =~ /$params{name2}/) { - $self->add_nagios_ok( - sprintf "output %s matches pattern %s", - $self->{genericsql}, $params{name2}); - } else { - $self->add_nagios_critical( - sprintf "output %s does not match pattern %s", - $self->{genericsql}, $params{name2}); + else { + $self->add_nagios_critical(sprintf "output %s does not match /%s/", + $data, $params{name2}); } } - } else { - $self->add_nagios( - # the first item in the list will trigger the threshold values - $self->check_thresholds($self->{genericsql}[0], 1, 5), - sprintf "%s: %s%s", - $params{name2} ? lc $params{name2} : lc $params{selectname}, - # float as float, integers as integers - join(" ", map { - (sprintf("%d", $_) eq $_) ? $_ : sprintf("%f", $_) - } @{$self->{genericsql}}), - $params{units} ? $params{units} : ""); - my $i = 0; - # workaround... getting the column names from the database would be nicer - my @names2_arr = split(/\s+/, $params{name2}); - foreach my $t (@{$self->{genericsql}}) { - $self->add_perfdata(sprintf "\'%s\'=%s%s;%s;%s", - $names2_arr[$i] ? lc $names2_arr[$i] : lc $params{selectname}, - # float as float, integers as integers - (sprintf("%d", $t) eq $t) ? $t : sprintf("%f", $t), - $params{units} ? $params{units} : "", - ($i == 0) ? $self->{warningrange} : "", - ($i == 0) ? $self->{criticalrange} : "" - ); - $i++; + else { + my $info = $params{sqlinfo} ? $row->[$params{sqlinfo}-1] : ''; + my $name = lc($params{sqlname} ? $row->[$params{sqlname}-1] + : ($params{name2} ? $params{name2} + : $params{selectname})); + $self->add_nagios( + $self->check_thresholds($data, 1, 5), + sprintf("%s: %s%s", + $name, + $info ? $info : join(" ", @{ $row }), + $params{units} ? $params{units} : "") + ); + my $i = 0; + # workaround: getting column names from the database would be nicer + my @names2 = split(/\s+/, $params{name2}); + my $prefix = lc($params{sqlname} ? $row->[$params{sqlname}-1].'_' + : ''); + for (my $i = 0; $i < scalar(@{ $row }); $i++) { + if ($params{sqlname} and ($i+1 == $params{sqlname})) { next; } + if ($params{sqlinfo} and ($i+1 == $params{sqlinfo})) { next; } + my $t = $row->[$i]; + $self->add_perfdata( + sprintf("\'%s%s\'=%s%s;%s;%s", + $prefix, + lc($names2[$i] ? $names2[$i] : $params{selectname}), + # float as float, integer as integer + (sprintf("%d", $t) eq $t) ? $t : sprintf("%f", $t), + $params{units} ? $params{units} : "", + ($i == $evalidx) ? $self->{warningrange} : "", + ($i == $evalidx) ? $self->{criticalrange} : "") + ); + } } } } elsif ($params{mode} =~ /^my::([^:.]+)/) { @@ -592,14 +623,6 @@ sub calculate_result { length join(" ", @{$self->{nagios}->{perfdata}}) > 200) { $multiline = 1; } - if ($multiline) { - my $num_ok = @{$self->{nagios}->{messages}->{ $ERRORS{"OK"} } }; - my $num_crit = @{$self->{nagios}->{messages}->{ $ERRORS{"WARNING"} }}; - my $num_warn = @{$self->{nagios}->{messages}->{ $ERRORS{"CRITICAL"} }}; - my $num_unk = @{$self->{nagios}->{messages}->{ $ERRORS{"UNKNOWN"} }}; - $self->{nagios_message} .= sprintf "%s ok, %s warnings, %s criticals and %s unknown\n", - $num_ok, $num_warn,$num_crit, $num_unk; - } my $all_messages = join(($multiline ? "\n" : ", "), map { join(($multiline ? "\n" : ", "), @{$self->{nagios}->{messages}->{$ERRORS{$_}}}) } grep { diff --git a/check_mssql_health/src/check_mssql_health-1.5.8.2/plugins-scripts/check_mssql_health.pl b/check_mssql_health/src/check_mssql_health-1.5.8.2/plugins-scripts/check_mssql_health.pl index f9f794b..9892982 100644 --- a/check_mssql_health/src/check_mssql_health-1.5.8.2/plugins-scripts/check_mssql_health.pl +++ b/check_mssql_health/src/check_mssql_health-1.5.8.2/plugins-scripts/check_mssql_health.pl @@ -102,7 +102,7 @@ my @modes = ( 'Elapsed time (in hours) since a database was last backupped' ], ['server::sql', 'sql', undef, - 'any sql command returning a single number' ], + 'any sql command' ], ['server::database::listdatabases', 'list-databases', undef, 'convenience function which lists all databases' ], @@ -174,19 +174,37 @@ EOUS If only a single database should be checked, use the --name parameter. The same applies to datafile-related modes. + Options specific to 'server::sql' mode: + + --sqleval + For results with multiple columns, this is the index (counting from + 1) of the column to evaluate with --warning and/or --critical. If + this option is missing, the statement is assumed to return only a + single column. + --sqlname + For results with multiple columns, this is the index (counting from + 1) of the column use as the output name (instead of --name2). If + this option is missing, only the first row will be parsed. + --sqlinfo + For results with multiple columns, this is the index (counting from + 1) of a column to present as a detailed explanation. + In mode sql you can url-encode the statement so you will not have to mess around with special characters in your Nagios service definitions. Instead of - --name="select count(*) from master..sysprocesses" + + --name="select count(*) from master..sysprocesses" + you can say - --name=select%20count%28%2A%29%20from%20master%2E%2Esysprocesses + + --name=select%20count%28%2A%29%20from%20master%2E%2Esysprocesses + For your convenience you can call check_mssql_health with the --encode option and it will encode the standard input. - You can find the full documentation for this plugin at - http://www.consol.de/opensource/nagios/check-mssql-health or - http://www.consol.com/opensource/nagios/check-mssql-health - + You can find the full documentation for this plugin at: + http://www.consol.de/opensource/nagios/check-mssql-health + or http://www.consol.com/opensource/nagios/check-mssql-health EOUS # @@ -257,6 +275,9 @@ my @params = ( "name=s", "name2=s", "regexp", + "sqleval=i", + "sqlname=i", + "sqlinfo=i", "perfdata", "warning=s", "critical=s", @@ -505,6 +526,9 @@ my %params = ( regexp => $commandline{regexp}, name => $commandline{name}, name2 => $commandline{name2} || $commandline{name}, + sqleval => $commandline{sqleval}, + sqlname => $commandline{sqlname}, + sqlinfo => $commandline{sqlinfo}, units => $commandline{units}, eyecandy => $commandline{eyecandy}, statefilesdir => $STATEFILESDIR,