diff options
author | Niko Tyni <ntyni@iki.fi> | 2005-02-13 20:23:04 +0100 |
---|---|---|
committer | Niko Tyni <ntyni@iki.fi> | 2005-02-13 20:23:04 +0100 |
commit | 6d76521656e91daa160bc8019828f1b68d7aa5dc (patch) | |
tree | aaa27615a0702942fa1606d9a5c89f0a3547467c /lib/Smokeping/probes/basefork.pm | |
parent | 6dba1afbe4b475a7d34f5ef867b7b37291cd1484 (diff) | |
download | smokeping-6d76521656e91daa160bc8019828f1b68d7aa5dc.tar.gz smokeping-6d76521656e91daa160bc8019828f1b68d7aa5dc.tar.xz |
Moved probes, matchers and ciscoRttMonMIB modules to lib/Smokeping.
Diffstat (limited to 'lib/Smokeping/probes/basefork.pm')
-rw-r--r-- | lib/Smokeping/probes/basefork.pm | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/lib/Smokeping/probes/basefork.pm b/lib/Smokeping/probes/basefork.pm new file mode 100644 index 0000000..4324e6d --- /dev/null +++ b/lib/Smokeping/probes/basefork.pm @@ -0,0 +1,266 @@ +package Smokeping::probes::basefork; + +=head1 301 Moved Permanently + +This is a Smokeping probe module. Please use the command + +C<smokeping -man Smokeping::probes::basefork> + +to view the documentation or the command + +C<smokeping -makepod Smokeping::probes::basefork> + +to generate the POD document. + +=cut + +use strict; +use base qw(Smokeping::probes::basevars); +use Symbol; +use Carp; +use IO::Select; +use POSIX; # for ceil() and floor() +use Config; # for signal names + +my $DEFAULTFORKS = 5; + +sub pod_hash { + return { + name => <<DOC, +Smokeping::probes::basefork - Yet Another Base Class for implementing SmokePing Probes +DOC + overview => <<DOC, +Like Smokeping::probes::basevars, but supports the probe-specific property `forks' +to determine how many processes should be run concurrently. The +targets are pinged one at a time, and the number of pings sent can vary +between targets. +DOC + description => <<DOC, +Not all pinger programs support testing multiple hosts in a single go like +fping(1). If the measurement takes long enough, there may be not enough time +perform all the tests in the time available. For example, if the test takes +30 seconds, measuring ten hosts already fills up the SmokePing default +five minute step. + +Thus, it may be necessary to do some of the tests concurrently. This module +defines the B<ping> method that forks the requested number of concurrent +processes and calls the B<pingone> method that derived classes must provide. + +The B<pingone> method is called with one argument: a hash containing +the target that is to be measured. The contents of the hash are +described in I<Smokeping::probes::basevars>(3pm). + +The number of concurrent processes is determined by the probe-specific +variable `forks' and is $DEFAULTFORKS by default. If there are more +targets than this value, another round of forks is done after the first +processes are finished. This continues until all the targets have been +tested. + +The timeout in which each child has to finish is set to 5 seconds +multiplied by the maximum number of 'pings' of the targets. You can set +the base timeout differently if you want to, using the timeout property +of the probe in the master config file (this again will be multiplied +by the maximum number of pings). The probe itself can also provide +another default value if desired by modifying the _default value of +the timeout variable. + +If the child isn't finished when the timeout occurs, it +will be killed along with any processes it has started. + +The number of pings sent can be specified in the target-specific variable +'pings'. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC, +Smokeping::probes::basevars(3pm), Smokeping::probes::EchoPing(3pm) +DOC + } +} + +my %signo; +my @signame; + +{ + # from perlipc man page + my $i = 0; + defined $Config{sig_name} || die "No sigs?"; + foreach my $name (split(' ', $Config{sig_name})) { + $signo{$name} = $i; + $signame[$i] = $name; + $i++; + } +} + +die("Missing TERM signal?") unless exists $signo{TERM}; +die("Missing KILL signal?") unless exists $signo{KILL}; + +sub pingone { + croak "pingone: this must be overridden by the subclass"; +} + +sub probevars { + my $class = shift; + my $h = $class->SUPER::probevars; + delete $h->{pings}; + return $class->_makevars($h, { + forks => { + _re => '\d+', + _example => 5, + _doc => "Run this many concurrent processes at maximum", + _default => $DEFAULTFORKS, + }, + timeout => { + _re => '\d+', + _example => 15, + _default => 5, + _doc => "How long a single 'ping' takes at maximum", + }, + }); +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + pings => { + _re => '\d+', + _example => 5, + _doc => <<DOC, +How many pings should be sent to each target, if different from the global +value specified in the Database section. Note that the number of pings in +the RRD files is fixed when they are originally generated, and if you +change this parameter afterwards, you'll have to delete the old RRD +files or somehow convert them. +DOC + }, + }); +} + +sub ping { + my $self = shift; + + my @targets = @{$self->targets}; + return unless @targets; + + my $forks = $self->{properties}{forks}; + + my $maxpings = 0; + my $maxtimeout = $self->{properties}{timeout}; + for (@targets) { + my $p = $self->pings($_); + $maxpings = $p if $p > $maxpings; + # some probes have a target-specific timeout variable + # dig out the maximum timeout + my $t = $_->{vars}{timeout}; + $maxtimeout = $t if $t > $maxtimeout; + } + + # we add 1 so that the probes doing their own timeout handling + # have time to do it even in the worst case + my $timeout = $maxpings * $maxtimeout + 1; + + $self->{rtts}={}; + $self->do_debug("forks $forks, timeout for each target $timeout"); + + while (@targets) { + my %targetlookup; + my %pidlookup; + my $s = IO::Select->new(); + my $starttime = time(); + for (1..$forks) { + last unless @targets; + my $t = pop @targets; + my $pid; + my $handle = gensym; + my $sleep_count = 0; + do { + $pid = open($handle, "-|"); + + unless (defined $pid) { + $self->do_log("cannot fork: $!"); + $self->fatal("bailing out") + if $sleep_count++ > 6; + sleep 10; + } + } until defined $pid; + if ($pid) { #parent + $s->add($handle); + $targetlookup{$handle} = $t; + $pidlookup{$handle} = $pid; + } else { #child + # we detach from the parent's process group + setpgrp(0, $$); + + my @times = $self->pingone($t); + print join(" ", @times), "\n"; + exit; + } + } + my $timeleft = $timeout - (time() - $starttime); + + while ($s->handles and $timeleft > 0) { + for my $ready ($s->can_read($timeleft)) { + $s->remove($ready); + my $response = <$ready>; + close $ready; + + chomp $response; + my @times = split(/ /, $response); + my $target = $targetlookup{$ready}; + my $tree = $target->{tree}; + $self->{rtts}{$tree} = \@times; + + $self->do_debug("$target->{addr}: got $response"); + } + $timeleft = $timeout - (time() - $starttime); + } + my @left = $s->handles; + for my $handle (@left) { + $self->do_log("$targetlookup{$handle}{addr}: timeout ($timeout s) reached, killing the probe."); + + # we kill the child's process group (negative signal) + # this should finish off the actual pinger process as well + + my $pid = $pidlookup{$handle}; + kill -$signo{TERM}, $pid; + sleep 1; + kill -$signo{KILL}, $pid; + + close $handle; + $s->remove($handle); + } + } +} + +# the "private" method that takes a "tree" argument is used by Smokeping.pm +sub _pings { + my $self = shift; + my $tree = shift; + my $vars = $self->vars($tree); + return $vars->{pings} if defined $vars->{pings}; + return $self->SUPER::pings(); +} + +# the "public" method that takes a "target" argument is used by the probes +sub pings { + my $self = shift; + my $target = shift; + return $self->SUPER::pings() unless ref $target; + return $self->_pings($target->{tree}); +} + +sub ProbeDesc { + return "Probe that can fork and doesn't override the ProbeDesc method"; +} + +sub pod_variables { + my $class = shift; + my $pod = $class->SUPER::pod_variables; + my $targetvars = $class->targetvars; + $pod .= "Supported target-specific variables:\n\n"; + $pod .= $class->_pod_variables($targetvars); + return $pod; +} + +1; |