Skip to content
Snippets Groups Projects
Commit b3c64077 authored by Theo Schlossnagle's avatar Theo Schlossnagle
Browse files

make this stuff object oriented.. but Resmon::Modules should really by Resmon::Module

git-svn-id: https://labs.omniti.com/resmon/trunk@20 8c0face9-b7db-6ec6-c4b3-d5f7145c7d55
parent 1ba116e5
No related branches found
No related tags found
No related merge requests found
package Resmon::Config;
use strict;
sub new {
my $class = shift;
my $filename = shift;
my $self = bless {
configfile => $filename,
}, $class;
open(CONF, "<$filename") || return undef;
my $current;
my $line = 0;
while(<CONF>) {
$line++;
next if /^\s*#/;
next if /^\s*$/;
if($current) {
if(/^\s*(\S+)\s*:\s*(.+)\s*$/) {
my %kvs;
$kvs{'type'} = $current;
$kvs{'object'} = $1;
my @params = split(/,/, $2);
grep { $kvs{$1} = $2 if /^\s*(\S+)\s*=>\s*(\S+)\s*$/ } @params;
my $object = bless \%kvs, "Resmon::Modules::$current";
push(@{$self->{Modules}->{$current}}, $object);
} elsif (/^\s*\}\s*$/) {
$current = undef;
} else {
die "Syntax Error on line $line\n";
}
} else {
if(/\s*(\S+)\s*\{/) {
$current = $1;
$self->{Modules}->{$current} = [];
next;
}
elsif(/\S*LIB\s+(\S+)\s*;?\s*/) {
eval "use lib '$1';";
next;
}
elsif(/\s*INTERVAL\s+(\d+)\s*;?\s*/) {
$self->{interval} = $1;
next;
}
elsif(/\s*STATUSFILE\s+(\S+)\s*;?\s*/) {
$self->{statusfile} = $1;
next;
}
else {
die "Syntax Error on line $line\n";
}
}
}
if($current) {
die "unclosed stanza\n";
}
return $self;
}
1;
#!/usr/bin/perl package Resmon::ExtComm;
use strict;
require Exporter;
use vars qw/@ISA @EXPORT/;
@ISA = qw/Exporter/;
@EXPORT = qw/cache_command/;
my %commhist; my %commhist;
my %commcache; my %commcache;
sub cache_command { sub cache_command($$;$) {
my $command = shift; my ($command, $expiry, $timeout) = @_;
my $expiry = shift; $timeout ||= $expiry;
my $now = time; my $now = time;
if($commhist{$command}>$now) { if($commhist{$command}>$now) {
return $commcache{$command}; return $commcache{$command};
} }
# TODO: timeouts
$commcache{$command} = `$command`; $commcache{$command} = `$command`;
$commhist{$command} = $now + $expiry; $commhist{$command} = $now + $expiry;
return $commcache{$command}; return $commcache{$command};
......
#!/usr/bin/perl package Resmon::Modules;
require 'ext_comm.pl'; use strict;
use Data::Dumper;
my %coderefs; my %coderefs;
...@@ -10,15 +11,16 @@ sub fetch_monitor { ...@@ -10,15 +11,16 @@ sub fetch_monitor {
my $type = shift; my $type = shift;
my $coderef = $coderefs{$type}; my $coderef = $coderefs{$type};
return $coderef if ($coderef); return $coderef if ($coderef);
if ( -r "$type.pl" ) { eval "use $type;";
require "$type.pl"; eval "use Resmon::Modules::$type;";
} return undef;
return $coderef = $coderefs{$type};
} }
sub register_monitor { sub register_monitor {
my ($type, $coderef) = @_; my ($type, $ref) = @_;
$coderefs{$type} = $coderef; if(ref $ref eq 'CODE') {
$coderefs{$type} = $ref;
}
print STDERR "$rmloading $type monitor\n"; print STDERR "$rmloading $type monitor\n";
} }
sub fresh_status { sub fresh_status {
...@@ -33,70 +35,79 @@ sub fresh_status { ...@@ -33,70 +35,79 @@ sub fresh_status {
sub set_status { sub set_status {
my $arg = shift; my $arg = shift;
$arg->{laststatus} = shift; $arg->{laststatus} = shift;
$arg->{lastmessage} = shift;
$arg->{lastupdate} = time; $arg->{lastupdate} = time;
return $arg->{laststatus}; if($arg->{laststatus} =~ /^([A-Z]+)\(([^\)]+)\)$/s) {
# This handles old-style modules that return just set status as
# STATE(message)
$arg->{laststatus} = $1;
$arg->{lastmessage} = $2;
}
return ($arg->{laststatus}, $arg->{lastmessage});
} }
#### Begin actual monitor functions #### #### Begin actual monitor functions ####
register_monitor('DATE', sub { package Resmon::Modules::DATE;
use vars qw/@ISA/;
@ISA = qw/Resmon::Modules/;
sub handler {
my $arg = shift; my $arg = shift;
my $os = fresh_status($arg); my $os = $arg->fresh_status();
return set_status($arg, "OK(".time().")"); return $arg->set_status("OK(".time().")");
}); }
package Resmon::Modules::DISK;
use Resmon::ExtComm qw/cache_command/;
use vars qw/@ISA/;
@ISA = qw/Resmon::Modules/;
register_monitor('DISK', sub { sub handler {
my $arg = shift; my $arg = shift;
my $os = fresh_status($arg); my $os = $arg->fresh_status();
return $os if $os; return $os if $os;
my $devorpart = $arg->{'object'}; my $devorpart = $arg->{'object'};
my $output = cache_command("df -k", 120); my $output = cache_command("df -k", 120);
my ($line) = grep(/$devorpart\s*/, split(/\n/, $output)); my ($line) = grep(/$devorpart\s*/, split(/\n/, $output));
if($line =~ /(\d+)%/) { if($line =~ /(\d+)%/) {
if($1 <= $arg->{'limit'}) { if($1 <= $arg->{'limit'}) {
return set_status($arg, "OK($1% full)"); return $arg->set_status("OK($1% full)");
} }
return set_status($arg, "BAD($1% full)"); return $arg->set_status("BAD($1% full)");
} }
return set_status($arg, "BAD(no data)"); return $arg->set_status("BAD(no data)");
}); }
register_monitor('A1000', sub { package Resmon::Modules::LOGFILE;
my $arg = shift; use vars qw/@ISA/;
my $os = fresh_status($arg); @ISA = qw/Resmon::Modules/;
return $os if $os;
my $unit = $arg->{'object'};
my $output = cache_command("/usr/lib/osa/bin/healthck -a", 500);
my ($line) = grep(/^$unit:/, split(/\n/, $output));
if ($line =~ /:\s+(.+)/) {
return set_status($arg, "OK($1)") if($1 eq $arg->{'status'});
return set_status($arg, "BAD($1)");
}
return set_status($arg, "BAD(no data)");
});
my %logfile_stats; my %logfile_stats;
register_monitor('LOGFILE', sub { sub handler {
my $arg = shift; my $arg = shift;
my $os = fresh_status($arg); my $os = $arg->fresh_status();
return $os if $os; return $os if $os;
my $file = $arg->{'object'}; my $file = $arg->{'object'};
my $match = $arg->{'match'}; my $match = $arg->{'match'};
my $errors; my $errors;
my $errorcount = 0; my $errorcount = 0;
my $start = 0; my $start = 0;
my @statinfo = stat($filename); my @statinfo = stat($file);
if($logfile_stats{$file}) { if($logfile_stats{$file}) {
my($dev, $ino, $size, $errs) = split(/-/, $logfile_stats{$file}); my($dev, $ino, $size, $errs) = split(/-/, $logfile_stats{$file});
if(($dev == $statinfo[0]) && ($ino == $statinfo[1])) { if(($dev == $statinfo[0]) && ($ino == $statinfo[1])) {
if($size == $statinfo[7]) { if($size == $statinfo[7]) {
return set_status($arg, "OK($errs)"); return $arg->set_status("OK($errs)");
} }
$start = $size; $start = $size;
$errorcount = $errs; $errorcount = $errs;
} }
} }
open(LOG, "<$file"); $logfile_stats{$file} = "$statinfo[0]-$statinfo[1]-$statinfo[7]-$errorcount";
seek(LOG, $size, 0); if(!open(LOG, "<$file")) {
return $arg->set_status("BAD(ENOFILE)");
}
seek(LOG, $statinfo[7], 0);
while(<LOG>) { while(<LOG>) {
chomp; chomp;
if(/$match/) { if(/$match/) {
...@@ -104,30 +115,38 @@ register_monitor('LOGFILE', sub { ...@@ -104,30 +115,38 @@ register_monitor('LOGFILE', sub {
$errorcount++; $errorcount++;
} }
} }
$logfile_stats{$file} = "$statinfo[0]-$statinfo[1]-$statinfo[7]-$errorcount";
if($errors) { if($errors) {
return set_status($arg, "BAD($errors)"); return $arg->set_status("BAD($errors)");
} }
return set_status($arg, "OK($errorcount)"); return $arg->set_status("OK($errorcount)");
}); }
package Resmon::Modules::FILEAGE;
use vars qw/@ISA/;
@ISA = qw/Resmon::Modules/;
register_monitor('FILEAGE', sub { sub handler {
my $arg = shift; my $arg = shift;
my $os = fresh_status($arg); my $os = $arg->fresh_status();
return $os if $os; return $os if $os;
my $file = $arg->{'object'}; my $file = $arg->{'object'};
my @statinfo = stat($file); my @statinfo = stat($file);
my $age = time() - $statinfo[9]; my $age = time() - $statinfo[9];
return set_status($arg, "BAD(to old $age seconds)") return $arg->set_status("BAD(to old $age seconds)")
if($arg->{maximum} && ($age > $arg->{maximum})); if($arg->{maximum} && ($age > $arg->{maximum}));
return set_status($arg, "BAD(to new $age seconds)") return $arg->set_status("BAD(to new $age seconds)")
if($arg->{minimum} && ($age > $arg->{minimum})); if($arg->{minimum} && ($age > $arg->{minimum}));
return set_status($arg, "OK($age)"); return $arg->set_status("OK($age)");
}); }
register_monitor('NETSTAT', sub { package Resmon::Modules::NETSTAT;
use Resmon::ExtComm qw/cache_command/;
use vars qw/@ISA/;
@ISA = qw/Resmon::Modules/;
sub handler {
my $arg = shift; my $arg = shift;
my $os = fresh_status($arg); my $os = $arg->fresh_status();
return $os if $os; return $os if $os;
my $output = cache_command("netstat -an", 30); my $output = cache_command("netstat -an", 30);
my @lines = split(/\n/, $output); my @lines = split(/\n/, $output);
...@@ -139,12 +158,12 @@ register_monitor('NETSTAT', sub { ...@@ -139,12 +158,12 @@ register_monitor('NETSTAT', sub {
@lines = grep(/^[\d\*\.]+\s+[\d\*\.+]\.$arg->{remoteport}/, @lines) @lines = grep(/^[\d\*\.]+\s+[\d\*\.+]\.$arg->{remoteport}/, @lines)
if($arg->{remoteport}); if($arg->{remoteport});
my $count = scalar(@lines); my $count = scalar(@lines);
return set_status($arg, "BAD($count)") return $arg->set_status("BAD($count)")
if($arg->{limit} && ($count > $arg->{limit})); if($arg->{limit} && ($count > $arg->{limit}));
return set_status($arg, "BAD($count)") return $arg->set_status("BAD($count)")
if($arg->{atleast} && ($count < $arg->{atleast})); if($arg->{atleast} && ($count < $arg->{atleast}));
return set_status($arg, "OK($count)"); return $arg->set_status("OK($count)");
}); }
$rmloading = "Demand loading"; $rmloading = "Demand loading";
1; 1;
#!/usr/bin/perl package Resmon::Modules::TCPSERVICE;
use Socket; use Socket;
use Fcntl; use Fcntl;
use IO::Select; use IO::Select;
use IO::Handle; use IO::Handle;
use Resmon::Modules;
register_monitor('TCPSERVICE', use vars qw/@ISA/;
sub { @ISA = qw/Resmon::Modules/;
my $arg = shift;
my $os = fresh_status($arg); sub handler {
my $self = shift;
my $os = $self->fresh_status();
return $os if $os; return $os if $os;
my $host = $arg->{host}; my $host = $self->{host};
my $port = $arg->{port}; my $port = $self->{port};
my $timeout = $arg->{timeout} || 5; my $timeout = $self->{timeout} || 5;
my $proto = getprotobyname('tcp'); my $proto = getprotobyname('tcp');
my $con = new IO::Select(); my $con = new IO::Select();
my $handle = new IO::Handle; my $handle = new IO::Handle;
...@@ -32,21 +35,21 @@ sub { ...@@ -32,21 +35,21 @@ sub {
close($handle); close($handle);
return "BAD(connect failed)"; return "BAD(connect failed)";
} }
print $handle $arg->{prepost}."\r\n" if ($arg->{prepost}); print $handle $self->{prepost}."\r\n" if ($self->{prepost});
($fd) = $con->can_read($timeout); ($fd) = $con->can_read($timeout);
if($fd == $handle) { if($fd == $handle) {
my $banner; my $banner;
chomp($banner = <$handle>); chomp($banner = <$handle>);
print $handle $arg->{post} if ($arg->{post}); print $handle $self->{post} if ($self->{post});
close($handle); close($handle);
$banner =~ s/([^\s\d\w.,;\/\\])/sprintf "\\%o", $1/eg; $banner =~ s/([^\s\d\w.,;\/\\])/sprintf "\\%o", $1/eg;
return "BAD($banner)" return "BAD($banner)"
if($arg->{match} && ($banner =! /$arg->{match}/)); if($self->{match} && ($banner =! /$self->{match}/));
return "OK($banner)"; return "OK($banner)";
} }
} }
close($handle); close($handle);
return "BAD(timeout)"; return "BAD(timeout)";
}); }
1; 1;
#!/usr/bin/perl #!/usr/bin/perl
push @INC, '/opt/resmon'; use lib '/opt/resmon/lib';
use strict; use strict;
use Time::HiRes qw( gettimeofday tv_interval sleep ); use Time::HiRes qw( gettimeofday tv_interval sleep );
use Data::Dumper;
use POSIX qw( setsid ); use POSIX qw( setsid );
use vars qw($opt_c $opt_d $opt_f $list); use Getopt::Long;
use vars qw($config_file $debug $status_file $config);
require 'getopts.pl'; use Resmon::Config;
require 'resmon_conf.pl'; use Resmon::ExtComm;
require 'resmon_code.pl'; use Resmon::Status;
use Resmon::Modules;
Getopts('c:df:'); GetOptions(
"c=s" => \$config_file,
"d" => \$debug,
"f=s" => \$status_file,
);
$opt_c ||= 'resmon.conf'; $config_file ||= 'resmon.conf';
die "Cannot open configuration file: $opt_c" unless (-r $opt_c); die "Cannot open configuration file: $config_file" unless (-r $config_file);
sub configure { sub configure {
$list = parse_config($opt_c); $config = Resmon::Config->new($config_file);
set_statusfile($opt_f) if($opt_f); $config->{statusfile} = $status_file if($status_file);
print Dumper($config) if($debug);
} }
unless($opt_d) { unless($debug) {
fork && exit; fork && exit;
setsid; setsid;
} }
configure(); configure();
$SIG{'HUP'} = \&configure; $SIG{'HUP'} = \&configure;
use Time::HiRes qw( gettimeofday tv_interval sleep );
my $rmlast = undef;
sub wait_interval {
$rmlast = [gettimeofday] unless defined($rmlast);
my $elapsed = $config->{interval} - tv_interval($rmlast);
if($elapsed > 0) {
sleep($elapsed);
}
$rmlast = [gettimeofday];
}
my $list = [];
my $status = Resmon::Status->new($config->{statusfile});
while(1) { while(1) {
foreach my $monobj (@$list) { while(my($module_name, $mod_configs) = each %{$config->{Modules}}) {
my $coderef = fetch_monitor($monobj->{'type'}); my $coderef = Resmon::Modules::fetch_monitor($module_name);
unless($coderef) { foreach my $monobj (@$mod_configs) {
$coderef = fetch_monitor($monobj->{'type'}); my $check_rv = 'BAD',
} my $check_mess = 'no data';
if($coderef) { if($coderef) {
print_statusfile($monobj->{'object'}."(".$monobj->{'type'} eval { ($check_rv, $check_mess) = $coderef->($monobj); };
.") :: ".$coderef->($monobj)."\n"); } else {
} else { eval { ($check_rv, $check_mess) = $monobj->handler(); };
print_statusfile($monobj->{'object'}."(".$monobj->{'type'} }
.") :: BAD(no monitor available)\n"); if($@) {
$status->store($module_name,$monobj->{'object'},'BAD',$@);
} else {
$status->store($module_name,$monobj->{'object'},$check_rv,$check_mess);
}
} }
} }
close_statusfile(); $status->close();
wait_interval(); wait_interval();
print "\n---- ".localtime(time)."----------\n" print "\n---- ".localtime(time)."----------\n"
unless open_statusfile(); unless $status->open();
} }
#!/usr/bin/perl
use Time::HiRes qw( gettimeofday tv_interval sleep );
my @conflist;
my $rmseconds;
my $rmlast = undef;
sub set_interval {
$rmseconds = shift;
}
sub wait_interval {
$rmlast = [gettimeofday] unless defined($rmlast);
my $elapsed = $rmseconds - tv_interval($rmlast);
if($elapsed > 0) {
sleep($elapsed);
}
$rmlast = [gettimeofday];
}
my $statusfile;
my $sfopened = 0;
sub set_statusfile {
$statusfile = shift;
$statusfile = undef if($statusfile eq '-');
}
sub open_statusfile {
return 0 unless $statusfile;
if(open(STAT, ">$statusfile.swap")) {
$sfopened = 1;
chmod 0644, "$statusfile.swap";
return 1;
}
return 0;
}
sub print_statusfile {
my $line = shift;
if($sfopened) {
print STAT $line;
} else {
print $line;
}
}
sub close_statusfile {
if($sfopened) {
close(STAT);
unlink("$statusfile");
link("$statusfile.swap", "$statusfile");
unlink("$statusfile.swap");
$sfopened = 0;
}
}
sub parse_config {
my $filename = shift;
open(CONF, "<$filename");
undef(@conflist);
while(<CONF>) {
next if /^\s*#/;
if(/\s*INTERVAL\s+(\d+)\s*;\s*/) {
set_interval($1);
next;
}
if(/\s*STATUSFILE\s+(\S+)\s*;\s*/) {
set_statusfile($1);
next;
}
if($current) {
if(/^\s*(\S+)\s*:\s*(.+)\s*$/) {
my %kvs;
$kvs{'type'} = $current;
$kvs{'object'} = $1;
my @params = split(/,/, $2);
grep { $kvs{$1} = $2 if /^\s*(\S+)\s*=>\s*(\S+)\s*$/ } @params;
push(@conflist, \%kvs);
} elsif (/^\s*\}\s*$/) {
$current = undef;
}
} else {
if(/\s*(\S+)\s*\{/) {
$current = $1;
next;
}
}
}
if($current) {
die "Error while parsing configuration file. $current clause unfinished";
}
return \@conflist;
}
1;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment