diff options
author | Tobi Oetiker <tobi@oetiker.ch> | 2005-02-11 21:48:36 +0100 |
---|---|---|
committer | Tobi Oetiker <tobi@oetiker.ch> | 2005-02-11 21:48:36 +0100 |
commit | b9ddd9310ea896a5e644f784013a386d9e7212a9 (patch) | |
tree | fb35c74000db18ce447689612f06103530a3645e /lib/probes | |
parent | 3623e33d0ae10eaeca653e00a3796495dbc0f713 (diff) | |
download | smokeping-b9ddd9310ea896a5e644f784013a386d9e7212a9.tar.gz smokeping-b9ddd9310ea896a5e644f784013a386d9e7212a9.tar.xz |
niko has revamped smokeping to parse configuration much more strictly. It is all documented in
software/doc/smokeping_upgrade.pod
Diffstat (limited to 'lib/probes')
28 files changed, 2140 insertions, 1756 deletions
diff --git a/lib/probes/AnotherDNS.pm b/lib/probes/AnotherDNS.pm index 7acc36d..9649514 100644 --- a/lib/probes/AnotherDNS.pm +++ b/lib/probes/AnotherDNS.pm @@ -1,77 +1,16 @@ package probes::AnotherDNS; -=head1 NAME +=head1 301 Moved Permanently -probes::AnotherDNS - Alternate DNS Probe - -=head1 SYNOPSIS - - *** Probes *** - + AnotherDNS - - *** Targets *** - probe = AnotherDNS - forks = 10 - - + First - menu = First - title = First Target - # .... - - ++ PROBE_CONF - lookup = www.mozilla.org - -=head1 DESCRIPTION - -Like DNS, but uses Net::DNS and Time::HiRes instead of dig. This probe does -*not* retry the request three times before it is considerd "lost", like dig and -other resolver do by default. If operating as caching Nameserver, BIND (and -maybe others) expect clients to retry the request if the answer is not in the -cache. So, ask the nameserver for something that he is authorative for if you -want measure the network packet loss correctly. - -If you have a really fast network and nameserver, you will notice that this -probe reports the query time in microsecond resolution. :-) - -=over - -=item forks - -The number of concurrent processes to be run. See probes::basefork(3pm) -for details. - -=back - -Supported target-level probe variables: - -=over - -=item lookup - -Name of the host to look up in the dns. +This is a Smokeping probe module. Please use the command -=item sleeptime +C<smokeping -man probes::AnotherDNS> -Time to sleep between two lookups in microseconds. Default is 500000. +to view the documentation or the command -=item recordtype +C<smokeping -makepod probes::AnotherDNS> -Record type to look up. Default is "A". - -=item timeout - -Timeout for a single request in seconds. Default is 5. - -=item port - -UDP Port to use. Default is 53. (Surprise!) - -=back - - -=head1 AUTHOR - -Christoph Heine E<lt>Christoph.Heine@HaDiKo.DEE<gt> +to generate the POD document. =cut @@ -91,10 +30,32 @@ use base qw(probes::basefork); use IPC::Open3; use Symbol; use Carp; -use Time::HiRes qw(usleep ualarm gettimeofday tv_interval); +use Time::HiRes qw(sleep ualarm gettimeofday tv_interval); use IO::Socket; use IO::Select; +sub pod_hash { + return { + name => <<DOC, +probes::AnotherDNS - Alternate DNS Probe +DOC + description => <<DOC, +Like DNS, but uses Net::DNS and Time::HiRes instead of dig. This probe does +*not* retry the request three times before it is considerd "lost", like dig and +other resolver do by default. If operating as caching Nameserver, BIND (and +maybe others) expect clients to retry the request if the answer is not in the +cache. So, ask the nameserver for something that he is authoritative for if you +want measure the network packet loss correctly. + +If you have a really fast network and nameserver, you will notice that this +probe reports the query time in microsecond resolution. :-) +DOC + authors => <<'DOC', +Christoph Heine <Christoph.Heine@HaDiKo.DE> +DOC + } +} + sub new($$$) { my $proto = shift; my $class = ref($proto) || $proto; @@ -113,14 +74,10 @@ sub pingone ($) { my $host = $target->{addr}; my $lookuphost = $target->{vars}{lookup}; - my $sleeptime = $target->{vars}{sleeptime}; + my $mininterval = $target->{vars}{mininterval}; my $recordtype = $target->{vars}{recordtype}; my $timeout = $target->{vars}{timeout}; my $port = $target->{vars}{port}; - $recordtype = "A" unless defined $recordtype; - $timeout = 5 unless defined $timeout; - $port = 53 unless defined $port; - $sleeptime = 500000 unless defined $sleeptime; $lookuphost = $target->{addr} unless defined $lookuphost; my $packet = Net::DNS::Packet->new( $lookuphost, $recordtype )->data; @@ -133,18 +90,22 @@ sub pingone ($) { my @times; + my $elapsed; for ( my $run = 0 ; $run < $self->pings($target) ; $run++ ) { + if (defined $elapsed) { + my $timeleft = $mininterval - $elapsed; + sleep $timeleft if $timeleft > 0; + } my $t0 = [gettimeofday]; $sock->send($packet); my ($ready) = $sel->can_read($timeout); my $t1 = [gettimeofday]; + $elapsed = tv_interval( $t0, $t1 ); if ( defined $ready ) { - my $time = tv_interval( $t0, $t1 ); - push @times, $time; + push @times, $elapsed; my $buf = ''; $ready->recv( $buf, &Net::DNS::PACKETSZ ); } - usleep($sleeptime); } @times = map { sprintf "%.10e", $_ } sort { $a <=> $b } grep { $_ ne "-" } @times; @@ -152,5 +113,45 @@ sub pingone ($) { return @times; } +sub probevars { + my $class = shift; + my $h = $class->SUPER::probevars; + delete $h->{timeout}; + return $h; +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + lookup => { + _doc => <<DOC, +Name of the host to look up in the dns. +DOC + _example => 'www.example.org', + }, + mininterval => { + _doc => <<DOC, +Minimum time between sending two lookup queries in (possibly fractional) seconds. +DOC + _default => .5, + _re => '(\d*\.)?\d+', + }, + recordtype => { + _doc => 'Record type to look up.', + _default => 'A', + }, + timeout => { + _doc => 'Timeout for a single request in seconds.', + _default => 5, + _re => '\d+', + }, + port => { + _doc => 'The UDP Port to use.', + _default => 53, + _re => '\d+', + }, + }); +} + 1; diff --git a/lib/probes/AnotherSSH.pm b/lib/probes/AnotherSSH.pm index bb1a8bb..1de6644 100644 --- a/lib/probes/AnotherSSH.pm +++ b/lib/probes/AnotherSSH.pm @@ -1,105 +1,41 @@ package probes::AnotherSSH; -=head1 NAME +=head1 301 Moved Permanently -probes::AnotherSSH - Another SSH probe - -=head1 SYNOPSIS - - *** Probes *** - + AnotherSSH - - *** Targets *** - probe = AnotherSSH - forks = 10 - - + First - menu = First - title = First Target - # .... - - ++ PROBE_CONF - greeting = SSH-Latecy-Measurement-Sorry-for-the-logfile-entry - sleeptime = 500000 - interval = established - timeout = 5 - -=head1 DESCRIPTION - -Latency measurement using SSH. This generates Logfile messages on the other -Host, so get permission from the owner first! - -=over - -=item forks - -The number of concurrent processes to be run. See probes::basefork(3pm) -for details. - -=back - -Supported target-level probe variables: - -=over - -=item greeting - -Greeting string to send to the SSH Server. This will appear in the Logfile. -Use this to make clear, who you are and what you are doing to avoid confusion. - -Also, don't use something that is a valid version string. This probe assumes -that the connection gets terminated because of protocol mismatch. - -=item sleeptime - -Time to sleep between two measurements in microsends. Default is 500000. - -=item interval - -The interval to measure - -=over - -=item connect - -Interval between connect() and the greeting string from the host. - -=item established - -Interval between our greeting message and the end of the connection -because of Protocol mismatch. This is the default. - -=item complete - -From connect() to the end of the connection. - -=back - -=item timeout - -Timeout for the connection. Default is 5. - -=item port - -Connect to this port. Default is 22. +This is a Smokeping probe module. Please use the command -=back +C<smokeping -man probes::AnotherSSH> +to view the documentation or the command -=head1 AUTHOR +C<smokeping -makepod probes::AnotherSSH> -Christoph Heine E<lt>Christoph.Heine@HaDiKo.DEE<gt> +to generate the POD document. =cut use strict; use base qw(probes::basefork); use Carp; -use Time::HiRes qw(usleep ualarm gettimeofday tv_interval); +use Time::HiRes qw(sleep ualarm gettimeofday tv_interval); use IO::Select; use Socket; use Fcntl; +sub pod_hash { + return { + name => <<DOC, +probes::AnotherSSH - Another SSH probe +DOC + description => <<DOC, +Latency measurement using SSH. This generates Logfile messages on the other +Host, so get permission from the owner first! +DOC + authors => <<'DOC', +Christoph Heine <Christoph.Heine@HaDiKo.DE> +DOC + } +} sub new($$$) { my $proto = shift; @@ -120,34 +56,30 @@ sub pingone ($) { my $host = $target->{addr}; # Time - my $sleeptime = $target->{vars}{sleeptime}; - $sleeptime = 500000 unless defined $sleeptime; + my $mininterval = $target->{vars}{mininterval}; # Our greeting string. my $greeting = $target->{vars}{greeting}; - $greeting = "SSH-Latency-Measurement-Sorry-for-this-logmessage" - unless defined $greeting; # Interval to measure my $interval = $target->{vars}{interval}; - $interval = "established" unless defined $interval; - if(not ( $interval eq "connect" or $interval eq "established" or $interval eq "complete")) { - $self->do_debug("Invalid interval parameter"); - return undef; - } # Connect to this port. my $port = $target->{vars}{port}; - $port = 22 unless defined $port; #Timeout for the select() calls. my $timeout = $target->{vars}{timeout}; - $timeout = 5 unless defined $timeout; my @times; # Result times + my $t0; for ( my $run = 0 ; $run < $self->pings($target) ; $run++ ) { - my ($t0,$t1,$t2,$t3); # Timestamps. + if (defined $t0) { + my $elapsed = tv_interval($t0, [gettimeofday]); + my $timeleft = $mininterval - $elapsed; + sleep $timeleft if $timeleft > 0; + } + my ($t1,$t2,$t3); # Timestamps. #Temporary variables to play with. my $ready; @@ -184,7 +116,7 @@ sub pingone ($) { close(Socket_Handle); next; } $nbytes = sysread( Socket_Handle, $buf, 1500 ); - if ($nbytes <= 0) { + if (not defined $nbytes or $nbytes <= 0) { $self->do_debug("Read nothing and Connection closed!"); close(Socket_Handle); next; } @@ -222,7 +154,6 @@ sub pingone ($) { } - usleep($sleeptime); } @times = map { sprintf "%.10e", $_ } sort { $a <=> $b } grep { $_ ne "-" } @times; @@ -230,5 +161,78 @@ sub pingone ($) { return @times; } +sub probevars { + my $class = shift; + my $h = $class->SUPER::probevars; + delete $h->{timeout}; + return $h; +} + +sub targetvars { + my $class = shift; + my $e = "="; + return $class->_makevars($class->SUPER::targetvars, { + greeting => { + _doc => <<DOC, +Greeting string to send to the SSH Server. This will appear in the Logfile. +Use this to make clear, who you are and what you are doing to avoid confusion. + +Also, don't use something that is a valid version string. This probe assumes +that the connection gets terminated because of protocol mismatch. +DOC + _default => "SSH-Latency-Measurement-Sorry-for-this-logmessage" , + }, + mininterval => { + _doc => "Minimum interval between the start of two connection attempts in (possibly fractional) seconds.", + _default => 0.5, + _re => '(\d*\.)?\d+', + }, + interval => { + _doc => <<DOC, +The interval to be measured. One of: + +${e}over + +${e}item connect + +Interval between connect() and the greeting string from the host. + +${e}item established + +Interval between our greeting message and the end of the connection +because of Protocol mismatch. This is the default. + +${e}item complete + +From connect() to the end of the connection. + +${e}back + +DOC + + _sub => sub { + my $interval = shift; + if(not ( $interval eq "connect" + or $interval eq "established" + or $interval eq "complete")) { + return "ERROR: Invalid interval parameter"; + } + return undef; + }, + _default => 'established', + }, + timeout => { + _doc => 'Timeout for the connection.', + _re => '\d+', + _default => 5, + }, + port => { + _doc => 'Connect to this port.', + _re => '\d+', + _default => 22, + }, + }); +} + 1; diff --git a/lib/probes/CiscoRTTMonDNS.pm b/lib/probes/CiscoRTTMonDNS.pm index 829a4de..691a39a 100644 --- a/lib/probes/CiscoRTTMonDNS.pm +++ b/lib/probes/CiscoRTTMonDNS.pm @@ -1,53 +1,51 @@ package probes::CiscoRTTMonDNS; -# please use -# pod2man CiscoRTTMonDNS.pm | nroff -man | more -# to view the manpage of this document -# +=head1 301 Moved Permanently +This is a Smokeping probe module. Please use the command -=head1 NAME +C<smokeping -man probes::CiscoRTTMonDNS> -probes::CiscoRTTMonDNS.pm - Probe for SmokePing - -=head1 SYNOPSIS - - *** Probes *** - + CiscoRTTMonDNS - + forks=50 +to view the documentation or the command - *** Targets *** - + MyRouter-DNSserver - menu = MyRouter->DNSserver - title = RTTMon DNS lookup of www.foobar.com.au on DNSserver - host = DNSserver.foobar.com.au - probe=CiscoRTTMonDNS - ++ PROBE_CONF - ioshost = RTTcommunity@Myrouter.foobar.com.au - name=www.foobar.com.au - iosint = 10.33.22.11 +C<smokeping -makepod probes::CiscoRTTMonDNS> -=head1 DESCRIPTION +to generate the POD document. -A probe for smokeping, which uses the ciscoRttMon MIB functionality ("Service Assurance Agent", "SAA") of Cisco IOS to time ( recursive, type A) DNS queries to a DNS server. +=cut -=head1 PARAMETERS +use strict; +use base qw(probes::basefork); +use Symbol; +use Carp; +use BER; +use SNMP_Session; +use SNMP_util "0.97"; +use ciscoRttMonMIB "0.2"; -The (mandatory) host parameter specifies the DNS server, which the router will use. This can be a DNS name, the smokeping host can resolve or a dotted-quad IP address. +my $e = "="; +sub pod_hash { + return { + name => <<DOC, +probes::CiscoRTTMonDNS.pm - Probe for SmokePing +DOC, + description => <<DOC, +A probe for smokeping, which uses the ciscoRttMon MIB functionality ("Service Assurance Agent", "SAA") of Cisco IOS to time ( recursive, type A) DNS queries to a DNS server. -The (mandatory) ioshost parameter specifies the Cisco router, which will send the DNS requests, as well as the SNMP community string on the router. +DOC -The (mandatory) name parameter is the DNS name to resolve. + notes => <<DOC, +${e}head2 host parameter -The (optional) iosint parameter is the source address for the DNS packets. This should be one of the active (!) IP addresses of the router to get results. IOS looks up the target host address in the forwarding table and then uses the interface(s) listed there to send the DNS packets. By default IOS uses the (primary) IP address on the sending interface as source address for packets originated by the router. +The host parameter specifies the DNS server, which the router will use. -=head1 IOS VERSIONS +${e}head2 IOS VERSIONS This probe only works with IOS 12.0(3)T or higher. It is recommended to test it on less critical routers first. -=head1 INSTALLATION +${e}head2 INSTALLATION -To install this probe copy ciscoRttMonMIB.pm to ($SMOKEPINGINSTALLDIR)/lib and CiscoRTTMonDNS.pm to ($SMOKEPINGINSTALLDIR)/lib/probes. +To install this probe copy ciscoRttMonMIB.pm to (\$SMOKEPINGINSTALLDIR)/lib and CiscoRTTMonDNS.pm to (\$SMOKEPINGINSTALLDIR)/lib/probes. The router(s) must be configured to allow read/write SNMP access. Sufficient is: @@ -60,35 +58,24 @@ If you want to be a bit more restrictive with SNMP write access to the router, t snmp-server community RTTCommunity view RttMon RW 2 The above configuration grants SNMP read-write only to 10.37.3.5 (the smokeping host) and only to the ciscoRttMon MIB tree. The probe does not need access to SNMP variables outside the RttMon tree. - -=head1 BUGS - +DOC + bugs => <<DOC, The probe does unnecessary DNS queries, i.e. more than configured in the "pings" variable, because the RTTMon MIB only allows to set a total time for all queries in one measurement run (one "life"). Currently the probe sets the life duration to "pings"*2+3 seconds (2 secs is the timeout value hardcoded into this probe). - -=head1 SEE ALSO - +DOC + see_also => <<DOC, http://people.ee.ethz.ch/~oetiker/webtools/smokeping/ + http://www.switch.ch/misc/leinen/snmp/perl/ The best source for background info on SAA is Cisco's documentation on http://www.cisco.com and the CISCO-RTTMON-MIB documentation, which is available at: -ftp://ftp.cisco.com/pub/mibs/v2/CISCO-RTTMON-MIB.my - - - -=head1 AUTHOR +ftp://ftp.cisco.com/pub/mibs/v2/CISCO-RTTMON-MIB.my +DOC + authors => <<DOC, Joerg.Kummer at Roche.com - -=cut - -use strict; -use base qw(probes::basefork); -use Symbol; -use Carp; -use BER; -use SNMP_Session; -use SNMP_util "0.97"; -use ciscoRttMonMIB "0.2"; +DOC + } +} my $pingtimeout =2; @@ -107,7 +94,6 @@ sub new($$$) sub ProbeDesc($){ my $self = shift; - my $bytes = $self->{properties}{packetsize} || 56; return "CiscoRTTMonDNS.pm"; } @@ -115,11 +101,6 @@ sub pingone ($$) { my $self = shift; my $target = shift; - croak ("please define 'ioshost' under the PROBE_CONF section of your target\n") - unless defined $target->{vars}{ioshost} ; - - croak ("please define 'name' under the PROBE_CONF section of your target\n") - unless defined $target->{vars}{name} ; my $name = $target->{vars}{name}; my $pings = $self->pings($target) || 20; @@ -279,5 +260,35 @@ sub DestroyData ($$) { &snmpset($host, "rttMonCtrlAdminStatus.$row", 'integer', 6); } +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + _mandatory => [ 'ioshost', 'name' ], + ioshost => { + _doc => <<DOC, +The (mandatory) ioshost parameter specifies the Cisco router, which will send the DNS requests, +as well as the SNMP community string on the router. +DOC + _example => 'RTTcommunity@Myrouter.foobar.com.au', + }, + name => { + _doc => "The (mandatory) name parameter is the DNS name to resolve.", + _example => 'www.foobar.com.au', + }, + iosint => { + _doc => <<DOC, +The (optional) iosint parameter is the source address for the DNS packets. +This should be one of the active (!) IP addresses of the router to get +results. IOS looks up the target host address in the forwarding table +and then uses the interface(s) listed there to send the DNS packets. By +default IOS uses the (primary) IP address on the sending interface as +source address for packets originated by the router. +DOC + _example => '10.33.22.11', + }, + }); +} + +=head1 1; diff --git a/lib/probes/CiscoRTTMonEchoICMP.pm b/lib/probes/CiscoRTTMonEchoICMP.pm index 9871813..c497ed9 100644 --- a/lib/probes/CiscoRTTMonEchoICMP.pm +++ b/lib/probes/CiscoRTTMonEchoICMP.pm @@ -1,49 +1,39 @@ package probes::CiscoRTTMonEchoICMP; -# please use -# pod2man CiscoRTTMonEchoICMP.pm | nroff -man | more -# to view the manpage of this document -# +=head1 301 Moved Permanently +This is a Smokeping probe module. Please use the command -=head1 NAME +C<smokeping -man probes::CiscoRTTMonEchoICMP> -probes::CiscoRTTMonEchoICMP - Probe for SmokePing +to view the documentation or the command -=head1 SYNOPSIS +C<smokeping -makepod probes::CiscoRTTMonEchoICMP> - *** Probes *** - + CiscoRTTMonEchoICMP - + forks=50 +to generate the POD document. - *** Targets *** - + MyRouter-PingVictim - menu = MyRouter->PingVictim - title = RTTMon ping from MyRouter to PingVictim - host = PingVictim.foobar.com.au - ++ PROBE_CONF - ioshost = RTTcommunity@Myrouter.foobar.com.au - iosint = 10.33.22.11 - packetsize = 1024 - tos = 160 +=cut -=head1 DESCRIPTION +use strict; +use base qw(probes::basefork); +use Symbol; +use Carp; +use BER; +use SNMP_Session; +use SNMP_util "0.97"; +use ciscoRttMonMIB "0.2"; +sub pod_hash { + my $e = "="; + return { + name => <<DOC, +probes::CiscoRTTMonEchoICMP - Probe for SmokePing +DOC + description => <<DOC, A probe for smokeping, which uses the ciscoRttMon MIB functionality ("Service Assurance Agent", "SAA") of Cisco IOS to measure ICMP echo ("ping") roundtrip times between a Cisco router and any IP address. - -=head1 PARAMETERS - -The (mandatory) host parameter specifies the IP host, which will be pinged by the router. This can be a DNS name, the smokeping host can resolve or a dotted-quad IP address. - -The (mandatory) ioshost parameter specifies the Cisco router, which will execute the pings, as well as the SNMP community string on the router. - -The (optional) packetsize parameter lets you configure the packetsize for the pings sent. The minimum is 8, the maximum 16392. Use the same number as with fping, if you want the same packet sizes being used on the network. Please note that the packesize must be specified under PROBE_CONF, all other definitions will be ignored. Default is 56 bytes. - -The (optional) iosint parameter is the source address for the pings sent. This should be one of the active (!) IP addresses of the router to get results. IOS looks up the target host address in the forwarding table and then uses the interface(s) listed there to send the ping packets. By default IOS uses the (primary) IP address on the sending interface as source address for a ping. The RTTMon MIB versions before IOS 12.0(3)T didn't support this parameter. - -The (optional) tos parameter specifies the value of the ToS byte in the IP header of the pings. Multiply DSCP values times 4 and Precedence values times 32 to calculate the ToS values to configure, e.g. ToS 160 corresponds to a DSCP value 40 and a Precedence value of 5. The RTTMon MIB versions before IOS 12.0(3)T didn't support this parameter. - -=head1 IOS VERSIONS +DOC + notes => <<DOC, +${e}head2 IOS VERSIONS It is highly recommended to use this probe with routers running IOS 12.0(3)T or higher and to test it on less critical routers first. I managed to crash a router with 12.0(9) quite consistently ( in IOS lingo 12.0(9) is older code than 12.0(3)T ). I did not observe crashes on higher IOS releases, but messages on the router like the one below, when multiple processes concurrently accessed the same router (this case was IOS 12.1(12b) ): @@ -52,9 +42,9 @@ Aug 20 07:30:14: %RTT-3-SemaphoreBadUnlock: %RTR: Attempt to unlock semaphore by Aug 20 07:35:15: %RTT-3-SemaphoreInUse: %RTR: Could not obtain a lock for RTR. Process 80 -=head1 INSTALLATION +${e}head2 INSTALLATION -To install this probe copy ciscoRttMonMIB.pm files to ($SMOKEPINGINSTALLDIR)/lib and CiscoRTTMonEchoICMP.pm to ($SMOKEPINGINSTALLDIR)/lib/probes. V0.97 or higher of Simon Leinen's SNMP_Session.pm is required. +To install this probe copy ciscoRttMonMIB.pm files to (\$SMOKEPINGINSTALLDIR)/lib and CiscoRTTMonEchoICMP.pm to (\$SMOKEPINGINSTALLDIR)/lib/probes. V0.97 or higher of Simon Leinen's SNMP_Session.pm is required. The router(s) must be configured to allow read/write SNMP access. Sufficient is: @@ -67,35 +57,23 @@ If you want to be a bit more restrictive with SNMP write access to the router, t snmp-server community RTTCommunity view RttMon RW 2 The above configuration grants SNMP read-write only to 10.37.3.5 (the smokeping host) and only to the ciscoRttMon MIB tree. The probe does not need access to SNMP variables outside the RttMon tree. - -=head1 BUGS - +DOC + bugs => <<DOC, The probe sends unnecessary pings, i.e. more than configured in the "pings" variable, because the RTTMon MIB only allows to set a total time for all pings in one measurement run (one "life"). Currently the probe sets the life duration to "pings"*2+3 seconds (2 secs is the ping timeout value hardcoded into this probe). - -=head1 SEE ALSO - +DOC + see_also => <<DOC, http://people.ee.ethz.ch/~oetiker/webtools/smokeping/ + http://www.switch.ch/misc/leinen/snmp/perl/ The best source for background info on SAA is Cisco's documentation on http://www.cisco.com and the CISCO-RTTMON-MIB documentation, which is available at: ftp://ftp.cisco.com/pub/mibs/v2/CISCO-RTTMON-MIB.my - - - -=head1 AUTHOR - +DOC + authors => <<DOC, Joerg.Kummer at Roche.com - -=cut - -use strict; -use base qw(probes::basefork); -use Symbol; -use Carp; -use BER; -use SNMP_Session; -use SNMP_util "0.97"; -use ciscoRttMonMIB "0.2"; +DOC + } +} my $pingtimeout =2; @@ -114,7 +92,7 @@ sub new($$$) sub ProbeDesc($){ my $self = shift; - my $bytes = $self->{properties}{packetsize} || 56; + my $bytes = $self->{properties}{packetsize}; return "CiscoRTTMonEchoICMP ($bytes Bytes)"; } @@ -122,12 +100,9 @@ sub pingone ($$) { my $self = shift; my $target = shift; - croak ("please define 'ioshost' under the PROBE_CONF section of your target\n") - unless defined $target->{vars}{ioshost} ; - my $pings = $self->pings($target) || 20; - my $tos = $target->{vars}{tos} || 0; - my $bytes = $target->{vars}{packetsize} || 56; + my $tos = $target->{vars}{tos}; + my $bytes = $target->{properties}{packetsize}; # use the proces ID as as row number to make this poll distinct on the router; my $row=$$; @@ -285,5 +260,63 @@ sub DestroyData ($$) { &snmpset($host, "rttMonCtrlAdminStatus.$row", 'integer', 6); } +sub probevars { + my $class = shift; + return $class->_makevars($class->SUPER::probevars, { + packetsize => { + _doc => <<DOC, +The packetsize parameter lets you configure the packetsize for the pings +sent. The minimum is 8, the maximum 16392. Use the same number as with +fping, if you want the same packet sizes being used on the network. +DOC + _default => 56, + _re => '\d+', + _sub => sub { + my $val = shift; + return "ERROR: packetsize must be between 8 and 16392" + unless $val >= 8 and $val <= 16392; + return undef; + }, + }, + }); +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + _mandatory => [ 'ioshost' ], + ioshost => { + _example => 'RTTcommunity@Myrouter.foobar.com.au', + _doc => <<DOC, +The (mandatory) ioshost parameter specifies the Cisco router, which will +execute the pings, as well as the SNMP community string on the router. +DOC + }, + iosint => { + _example => '10.33.22.11', + _doc => <<DOC, +The (optional) iosint parameter is the source address for the pings +sent. This should be one of the active (!) IP addresses of the router to +get results. IOS looks up the target host address in the forwarding table +and then uses the interface(s) listed there to send the ping packets. By +default IOS uses the (primary) IP address on the sending interface as +source address for a ping. The RTTMon MIB versions before IOS 12.0(3)T +didn't support this parameter. +DOC + }, + tos => { + _example => 160, + _default => 0, + _doc => <<DOC, +The (optional) tos parameter specifies the value of the ToS byte in +the IP header of the pings. Multiply DSCP values times 4 and Precedence +values times 32 to calculate the ToS values to configure, e.g. ToS 160 +corresponds to a DSCP value 40 and a Precedence value of 5. The RTTMon +MIB versions before IOS 12.0(3)T didn't support this parameter. +DOC + }, + }); +} + 1; diff --git a/lib/probes/CiscoRTTMonTcpConnect.pm b/lib/probes/CiscoRTTMonTcpConnect.pm index c0b07bf..84f2fe6 100644 --- a/lib/probes/CiscoRTTMonTcpConnect.pm +++ b/lib/probes/CiscoRTTMonTcpConnect.pm @@ -1,56 +1,45 @@ package probes::CiscoRTTMonTcpConnect; -# please use -# pod2man CiscoRTTMonTcpConnect.pm | nroff -man | more -# to view the manpage of this document -# +=head1 301 Moved Permanently +This is a Smokeping probe module. Please use the command -=head1 NAME +C<smokeping -man probes::CiscoRTTMonTcpConnect> -probes::CiscoRTTMonTcpConnect - Probe for SmokePing +to view the documentation or the command -=head1 SYNOPSIS +C<smokeping -makepod probes::CiscoRTTMonTcpConnect> - *** Probes *** - + CiscoRTTMonTcpConnect - + forks=50 +to generate the POD document. - *** Targets *** - + MyRouter-TCPVictim - menu = MyRouter->TCPVictim - title = RTTMon TCP connect from MyRouter to TCPVictim - host = TCPVictim.foobar.com.au - probe=CiscoRTTMonTcpConnect - ++ PROBE_CONF - ioshost = RTTcommunity@Myrouter.foobar.com.au - iosint = 10.33.22.11 - tos = 160 - port = 23 +=cut -=head1 DESCRIPTION +use strict; +use base qw(probes::basefork); +use Symbol; +use Carp; +use BER; +use SNMP_Session; +use SNMP_util "0.97"; +use ciscoRttMonMIB "0.2"; +sub pod_hash { + my $e = "="; + return { + name => <<DOC, +probes::CiscoRTTMonTcpConnect - Probe for SmokePing +DOC + description => <<DOC, A probe for smokeping, which uses the ciscoRttMon MIB functionality ("Service Assurance Agent", "SAA") of Cisco IOS to measure TCP connect times between a Cisco router and a TCP server. The measured value is the time is the time to establish a TCP session, i.e. the time between the initial "SYN" TCP packet of the router and the "SYN ACK" packet of the host. The router terminates the TCP session immediately after the reception of "SYN ACK" with a "FIN" packet. - -=head1 PARAMETERS - -The (mandatory) host parameter specifies the IP host, which the router will connect to. This can be a DNS name, the smokeping host can resolve or a dotted-quad IP address. - -The (mandatory) ioshost parameter specifies the Cisco router, which will establish the TCP connections as well as the SNMP community string on the router. - -The (optional) port parameter lets you configure the destination TCP port on the host. The default is the http port 80. - -The (optional) iosint parameter is the source address for the TCP connections. This should be one of the active (!) IP addresses of the router to get results. IOS looks up the target host address in the forwarding table and then uses the interface(s) listed there to send the TCP packets. By default IOS uses the (primary) IP address on the sending interface as source address for a connection. - -The (optional) tos parameter specifies the value of the ToS byte in the IP header of the packets from the router. Multiply DSCP values times 4 and Precedence values times 32 to calculate the ToS values to configure, e.g. ToS 160 corresponds to a DSCP value 40 and a Precedence value of 5. Please note that this will not influence the ToS value in the packets sent by the the host. - -=head1 IOS VERSIONS +DOC + notes => <<DOC, +${e}head2 IOS VERSIONS This probe only works with Cisco IOS 12.0(3)T or higher. It is recommended to test it on less critical routers first. -=head1 INSTALLATION +${e}head2 INSTALLATION -To install this probe copy ciscoRttMonMIB.pm to ($SMOKEPINGINSTALLDIR)/lib and CiscoRTTMonTcpConnect.pm to ($SMOKEPINGINSTALLDIR)/lib/probes. V0.97 or higher of Simon Leinen's SNMP_Session.pm is required. +To install this probe copy ciscoRttMonMIB.pm to (\$SMOKEPINGINSTALLDIR)/lib and CiscoRTTMonTcpConnect.pm to (\$SMOKEPINGINSTALLDIR)/lib/probes. V0.97 or higher of Simon Leinen's SNMP_Session.pm is required. The router(s) must be configured to allow read/write SNMP access. Sufficient is: @@ -63,35 +52,23 @@ If you want to be a bit more restrictive with SNMP write access to the router, t snmp-server community RTTCommunity view RttMon RW 2 The above configuration grants SNMP read-write only to 10.37.3.5 (the smokeping host) and only to the ciscoRttMon MIB tree. The probe does not need access to SNMP variables outside the RttMon tree. - -=head1 BUGS - +DOC + bugs => <<DOC, The probe establishes unnecessary connections, i.e. more than configured in the "pings" variable, because the RTTMon MIB only allows to set a total time for all connections in one measurement run (one "life"). Currently the probe sets the life duration to "pings"*2+3 seconds (2 secs is the timeout value hardcoded into this probe). - -=head1 SEE ALSO - +DOC + see_also => <<DOC, http://people.ee.ethz.ch/~oetiker/webtools/smokeping/ + http://www.switch.ch/misc/leinen/snmp/perl/ The best source for background info on SAA is Cisco's documentation on http://www.cisco.com and the CISCO-RTTMON-MIB documentation, which is available at: ftp://ftp.cisco.com/pub/mibs/v2/CISCO-RTTMON-MIB.my - - - -=head1 AUTHOR - +DOC + authors => <<DOC, Joerg.Kummer at Roche.com - -=cut - -use strict; -use base qw(probes::basefork); -use Symbol; -use Carp; -use BER; -use SNMP_Session; -use SNMP_util "0.97"; -use ciscoRttMonMIB "0.2"; +DOC + } +} my $pingtimeout =2; @@ -110,20 +87,16 @@ sub new($$$) sub ProbeDesc($){ my $self = shift; - my $bytes = $self->{properties}{packetsize} || 56; - return "CiscoRTTMonTcpConnect ($bytes Bytes)"; + return "CiscoRTTMonTcpConnect"; } sub pingone ($$) { my $self = shift; my $target = shift; - croak ("please define 'ioshost' under the PROBE_CONF section of your target\n") - unless defined $target->{vars}{ioshost} ; - my $pings = $self->pings($target) || 20; - my $tos = $target->{vars}{tos} || 0; - my $port = $target->{vars}{port} || 80; + my $tos = $target->{vars}{tos}; + my $port = $target->{vars}{port}; # use the proces ID as as row number to make this poll distinct on the router; my $row=$$; @@ -281,5 +254,52 @@ sub DestroyData ($$) { &snmpset($host, "rttMonCtrlAdminStatus.$row", 'integer', 6); } +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + _mandatory => [ 'ioshost' ], + ioshost => { + _example => 'RTTcommunity@Myrouter.foobar.com.au', + _doc => <<DOC, +The (mandatory) ioshost parameter specifies the Cisco router, which will +establish the TCP connections as well as the SNMP community string on +the router. +DOC + }, + port => { + _default => 80, + _re => '\d+', + _doc => <<DOC, +The (optional) port parameter lets you configure the destination TCP +port on the host. The default is the http port 80. +DOC + }, + iosint => { + _example => '10.33.22.11', + _doc => <<DOC, +The (optional) iosint parameter is the source address for the TCP +connections. This should be one of the active (!) IP addresses of the +router to get results. IOS looks up the target host address in the +forwarding table and then uses the interface(s) listed there to send +the TCP packets. By default IOS uses the (primary) IP address on the +sending interface as source address for a connection. +DOC + }, + tos => { + _default => 0, + _example => 160, + _re => '\d+', + _doc => <<DOC, +The (optional) tos parameter specifies the value of the ToS byte in the +IP header of the packets from the router. Multiply DSCP values times 4 +and Precedence values times 32 to calculate the ToS values to configure, +e.g. ToS 160 corresponds to a DSCP value 40 and a Precedence value of +5. Please note that this will not influence the ToS value in the packets +sent by the the host. +DOC + }, + }); +} + 1; diff --git a/lib/probes/Curl.pm b/lib/probes/Curl.pm index 9dc4d31..c7aaccb 100644 --- a/lib/probes/Curl.pm +++ b/lib/probes/Curl.pm @@ -1,105 +1,102 @@ package probes::Curl; -my $DEFAULTBIN = "/usr/bin/curl"; - -=head1 NAME - -probes::Curl - a curl(1) probe for SmokePing - -=head1 OVERVIEW - -Fetches an HTTP or HTTPS URL using curl(1). - -=head1 SYNOPSYS - - *** Probes *** - + Curl - - binary = /usr/bin/curl # default value - - *** Targets *** - - probe = Curl - forks = 10 - - menu = Top - title = Top Menu - remark = Top Menu Remark - - + PROBE_CONF +=head1 301 Moved Permanently - + First - menu = First - title = First Target - host = some.host +This is a Smokeping probe module. Please use the command - # PROBE_CONF can be overridden here - ++ PROBE_CONF - agent = "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.2.1) Gecko/20021130" - url = https://some.host/some/where +C<smokeping -man probes::Curl> -=head1 DESCRIPTION +to view the documentation or the command -Supported probe-specific variables: +C<smokeping -makepod probes::Curl> -=over +to generate the POD document. -=item binary - -The location of your curl binary. - -=item forks - -The number of concurrent processes to be run. See probes::basefork(3pm) -for details. - -=item url - -The URL to fetch. Can be any one that curl supports. +=cut -=back +use strict; +use base qw(probes::basefork); +use Carp; -Supported target-level probe variables -(see curl(1) for details of the options): +my $DEFAULTBIN = "/usr/bin/curl"; -=over +sub pod_hash { + return { + name => "probes::Curl - a curl(1) probe for SmokePing", + overview => "Fetches an HTTP or HTTPS URL using curl(1).", + description => "(see curl(1) for details of the options below)", + authors => <<'DOC', + Gerald Combs <gerald [AT] ethereal.com> + Niko Tyni <ntyni@iki.fi> +DOC + notes => <<DOC, +The URL to be tested used to be specified by the variable 'url' in earlier +versions of Smokeping, and the 'host' setting did not influence it in any +way. The variable name has now been changed to 'urlformat', and it can +(and in most cases should) contain a placeholder for the 'host' variable. +DOC + see_also => "curl(1), probes::Curl(3pm) etc., http://curl.haxx.se/", + } +} -=item agent +sub probevars { + my $class = shift; + my $h = $class->SUPER::probevars; + delete $h->{timeout}; + return $class->_makevars($h, { + binary => { + _doc => "The location of your curl binary.", + _default => $DEFAULTBIN, + _sub => sub { + my $val = shift; + return "ERROR: Curl 'binary' $val does not point to an executable" + unless -f $val and -x _; + return undef; + }, + }, + }); +} +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + _mandatory => [ 'urlformat' ], + agent => { + _doc => <<DOC, The "-A" curl(1) option. This is a full HTTP User-Agent header including the words "User-Agent:". It should be enclosed in quotes if it contains -shell metacharacters - -=item ssl2 - -The "-2" curl(1) option. Force SSL2. - -=item timeout - -The "-m" curl(1) option. Maximum timeout in seconds. - -=item interface - +shell metacharacters. +DOC + _example => '"User-Agent: Lynx/2.8.4rel.1 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/0.9.6c"', + }, + timeout => { + _doc => qq{The "-m" curl(1) option. Maximum timeout in seconds.}, + _re => '\d+', + _example => 10, + _default => 5, + }, + interface => { + _doc => <<DOC, The "--interface" curl(1) option. Bind to a specific interface, IP address or host name. +DOC + _example => 'eth0', + }, + ssl2 => { + _doc => qq{The "-2" curl(1) option. Force SSL2.}, + _example => 1, + }, + urlformat => { + _doc => <<DOC, +The template of the URL to fetch. Can be any one that curl supports. +Any occurrence of the string '%host%' will be replaced with the +host to be probed. +DOC + _example => "http://%host%/", + }, + }); +} -=back - -=head1 AUTHORS - -Gerald Combs E<lt>gerald [AT] ethereal.comE<gt> -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 SEE ALSO - -curl(1), probes::Curl(3pm) etc., http://curl.haxx.se/ - -=cut - -use strict; -use base qw(probes::basefork); -use Carp; -# # derived class will mess with this through the 'features' method below my $featurehash = { agent => "-A", @@ -121,12 +118,6 @@ sub new { $self->_init if $self->can('_init'); - unless (defined $self->{properties}{binary}) { - $self->{properties}{binary} = $DEFAULTBIN; - } - croak "ERROR: Curl 'binary' $self->{properties}{binary} does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; - $self->test_usage; return $self; @@ -156,15 +147,6 @@ sub ProbeDesc($) { return "HTTP, HTTPS, and FTP URLs using curl(1)"; } -# This can be overridden to tag the port number to the address -# in derived classes (namely Curl) -sub make_host { - my $self = shift; - my $target = shift; - return $target->{addr}; -} - - # other than host, count and protocol-specific args come from here sub make_args { my $self = shift; @@ -174,7 +156,6 @@ sub make_args { for (keys %arghash) { my $val = $target->{vars}{$_}; - $val = $self->{properties}{$_} unless defined $val; push @args, ($arghash{$_}, $val) if defined $val; } return @args; @@ -199,8 +180,9 @@ sub make_commandline { my $count = shift; my @args = $self->make_args($target); - my $url = $target->{vars}{url}; - $url = "" unless defined $url; + my $url = $target->{vars}{urlformat}; + my $host = $target->{addr}; + $url =~ s/%host%/$host/g; push @args, $self->proto_args($target); return ($self->{properties}{binary}, @args, $url); diff --git a/lib/probes/DNS.pm b/lib/probes/DNS.pm index 877ca97..1b4ec5c 100644 --- a/lib/probes/DNS.pm +++ b/lib/probes/DNS.pm @@ -1,78 +1,47 @@ package probes::DNS; -=head1 NAME +=head1 301 Moved Permanently -probes::DNS - Name Service Probe for SmokePing +This is a Smokeping probe module. Please use the command -=head1 SYNOPSIS +C<smokeping -man probes::DNS> - *** Probes *** - + DNS - binary = /usr/bin/dig +to view the documentation or the command - *** Targets *** - probe = DNS - forks = 10 +C<smokeping -makepod probes::DNS> - + First - menu = First - title = First Target - # .... +to generate the POD document. - ++ PROBE_CONF - lookup=www.mozilla.org +=cut -=head1 DESCRIPTION +use strict; +use base qw(probes::basefork); +use IPC::Open3; +use Symbol; +use Carp; +sub pod_hash { + return { + name => <<DOC, +probes::DNS - Name Service Probe for SmokePing +DOC + description => <<DOC, Integrates dig as a probe into smokeping. The variable B<binary> must point to your copy of the dig program. If it is not installed on your system yet, you should install bind-utils >= 9.0.0. The Probe asks the given host n-times for it's name. Where n is the amount specified in the config File. - -Supported probe-specific variables: - -=over - -=item binary - -The location of your dig binary. - -=item forks - -The number of concurrent processes to be run. See probes::basefork(3pm) -for details. - -=back - -Supported target-level probe variables: - -=over - -=item lookup - -Name of the host to look up in the dns. - -=back - - -=head1 AUTHOR - -Igor Petrovski E<lt>pigor@myrealbox.comE<gt>, -Carl Elkins E<lt>carl@celkins.org.ukE<gt>, -Andre Stolze E<lt>stolze@uni-muenster.deE<gt>, -Niko Tyni E<lt>ntyni@iki.fiE<gt>, -Chris Poetzel<lt>cpoetzel@anl.gov<gt> - - -=cut - -use strict; -use base qw(probes::basefork); -use IPC::Open3; -use Symbol; -use Carp; +DOC + authors => <<'DOC', + Igor Petrovski <pigor@myrealbox.com>, + Carl Elkins <carl@celkins.org.uk>, + Andre Stolze <stolze@uni-muenster.de>, + Niko Tyni <ntyni@iki.fi>, + Chris Poetzel<cpoetzel@anl.gov> +DOC + }; +} my $dig_re=qr/query time:\s+([0-9.]+)\smsec.*/i; @@ -85,11 +54,6 @@ sub new($$$) # no need for this if we run as a cgi unless ( $ENV{SERVER_SOFTWARE} ) { - croak "ERROR: DNS 'binary' not defined in FPing probe definition" - unless defined $self->{properties}{binary}; - - croak "ERROR: DNS 'binary' does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; my $call = "$self->{properties}{binary} localhost"; my $return = `$call 2>&1`; if ($return =~ m/$dig_re/s){ @@ -103,6 +67,32 @@ sub new($$$) return $self; } +sub probevars { + my $class = shift; + return $class->_makevars($class->SUPER::probevars, { + _mandatory => [ 'binary' ], + binary => { + _doc => "The location of your dig binary.", + _example => '/usr/bin/dig', + _sub => sub { + my $val = shift; + return "ERROR: DNS 'binary' does not point to an executable" + unless -f $val and -x _; + return undef; + }, + }, + }); +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + lookup => { _doc => "Name of the host to look up in the dns.", + _example => "www.example.org", + }, + }); +} + sub ProbeDesc($){ my $self = shift; return "DNS requests"; @@ -120,7 +110,6 @@ sub pingone ($){ my $lookuphost = $target->{vars}{lookup}; $lookuphost = $target->{addr} unless defined $lookuphost; - #my $host = $target->{addr}; my $query = "$self->{properties}{binary} \@$host $lookuphost"; my @times; diff --git a/lib/probes/EchoPing.pm b/lib/probes/EchoPing.pm index d7558fa..843c9a1 100644 --- a/lib/probes/EchoPing.pm +++ b/lib/probes/EchoPing.pm @@ -1,134 +1,54 @@ package probes::EchoPing; -my $DEFAULTBIN = "/usr/bin/echoping"; - -=head1 NAME - -probes::EchoPing - an echoping(1) probe for SmokePing - -=head1 OVERVIEW - -Measures TCP or UDP echo (port 7) roundtrip times for SmokePing. Can also be -used as a base class for other echoping(1) probes. - -=head1 SYNOPSYS - - *** Probes *** - + EchoPing - - binary = /usr/bin/echoping # default value - - *** Targets *** - - probe = EchoPing - forks = 10 - - menu = Top - title = Top Menu - remark = Top Menu Remark - - + PROBE_CONF - - # none of these are mandatory - timeout = 1 - waittime = 1 - udp = no - size = 510 - tos = 0xa0 - priority = 6 - - + First - menu = First - title = First Target - host = router.example.com - - # PROBE_CONF can be overridden here - ++ PROBE_CONF - size = 300 - -=head1 DESCRIPTION - -Supported probe-specific variables: - -=over - -=item binary - -The location of your echoping binary. - -=item forks - -The number of concurrent processes to be run. See probes::basefork(3pm) -for details. - -=back - -Supported target-level probe variables -(see echoping(1) for details of the options): - -=over - -=item timeout - -The "-t" echoping(1) option. - -=item waittime - -The "-w" echoping(1) option. - -=item size - -The "-s" echoping(1) option. - -=item udp +=head1 301 Moved Permanently -The "-u" echoping(1) option. Values other than '0' and 'no' enable UDP. +This is a Smokeping probe module. Please use the command -=item fill +C<smokeping -man probes::EchoPing> -The "-f" echoping(1) option. +to view the documentation or the command -=item priority +C<smokeping -makepod probes::EchoPing> -The "-p" echoping(1) option. +to generate the POD document. -=item tos - -The "-P" echoping(1) option. - -=item ipversion - -The IP protocol used. Possible values are "4" and "6". -Passed to echoping(1) as the "-4" or "-6" options. - -=item extraopts - -Any extra options specified here will be passed unmodified to echoping(1). +=cut -=back +use strict; +use base qw(probes::basefork); +use Carp; -=head1 BUGS +my $DEFAULTBIN = "/usr/bin/echoping"; +sub pod_hash { + return { + name => <<DOC, +probes::EchoPing - an echoping(1) probe for SmokePing +DOC + overview => <<DOC, +Measures TCP or UDP echo (port 7) roundtrip times for SmokePing. Can also be +used as a base class for other echoping(1) probes. +DOC + description => <<DOC, +See echoping(1) for details of the options below. +DOC + bugs => <<DOC, Should we test the availability of the service at startup? After that it's too late to complain. The location of the echoping binary should probably be a global variable instead of a probe-specific one. As things are, every EchoPing -derived probe -has to declare it if the default (/usr/bin/echoping) isn't correct. - -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 SEE ALSO - +has to declare it if the default ($DEFAULTBIN) isn't correct. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC, echoping(1), probes::EchoPingHttp(3pm) etc., http://echoping.sourceforge.net/ +DOC + } +} -=cut - -use strict; -use base qw(probes::basefork); -use Carp; # # derived class will mess with this through the 'features' method below my $featurehash = { @@ -154,12 +74,6 @@ sub new { $self->_init if $self->can('_init'); - unless (defined $self->{properties}{binary}) { - $self->{properties}{binary} = $DEFAULTBIN; - } - croak "ERROR: EchoPing 'binary' $self->{properties}{binary} does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; - $self->test_usage; return $self; @@ -207,11 +121,9 @@ sub make_args { for (keys %arghash) { my $val = $target->{vars}{$_}; - $val = $self->{properties}{$_} unless defined $val; push @args, ($arghash{$_}, $val) if defined $val; } push @args, $self->ipversion_arg($target); - push @args, $self->{properties}{extraopts} if exists $self->{properties}{extraopts}; push @args, $target->{vars}{extraopts} if exists $target->{vars}{extraopts}; return @args; @@ -240,7 +152,6 @@ sub udp_arg { my @args; my $udp = $target->{vars}{udp}; - $udp = $self->{properties}{udp} unless defined $udp; push @args, "-u" if (defined $udp and $udp ne "no" and $udp ne "0"); return @args; @@ -250,7 +161,6 @@ sub ipversion_arg { my $self = shift; my $target = shift; my $vers = $target->{vars}{ipversion}; - $vers = $self->{properties}{ipversion} unless defined $vers; if (defined $vers and $vers =~ /^([46])$/) { return ("-" . $1); } else { @@ -295,9 +205,78 @@ sub pingone { /^Elapsed time: (\d+\.\d+) seconds/ and push @times, $1; } close P; - carp "WARNING: $cmd was not happy: $echoret\n" if $?; + $self->do_log("WARNING: $cmd was not happy: $echoret") if $?; # carp("Got @times") if $self->debug; return sort { $a <=> $b } @times; } +sub probevars { + my $class = shift; + my $h = $class->SUPER::probevars; + delete $h->{timeout}; + return $class->_makevars($h, { + binary => { + _doc => "The location of your echoping binary.", + _default => $DEFAULTBIN, + _sub => sub { + my $val = shift; + -x $val or return "ERROR: binary '$val' is not executable"; + return undef; + }, + }, + }); +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + timeout => { + _doc => 'The "-t" echoping(1) option.', + _example => 1, + _default => 5, + _re => '(\d*\.)?\d+', + }, + waittime => { + _doc => 'The "-w" echoping(1) option.', + _example => 1, + _re => '\d+', + }, + size => { + _doc => 'The "-s" echoping(1) option.', + _example => 510, + _re => '\d+', + }, + udp => { + _doc => q{The "-u" echoping(1) option. Values other than '0' and 'no' enable UDP.}, + _example => 'no', + }, + fill => { + _doc => 'The "-f" echoping(1) option.', + _example => 'A', + _re => '.', + }, + priority => { + _doc => 'The "-p" echoping(1) option.', + _example => 6, + _re => '\d+', + }, + tos => { + _doc => 'The "-P" echoping(1) option.', + _example => '0xa0', + }, + ipversion => { + _doc => <<DOC, +The IP protocol used. Possible values are "4" and "6". +Passed to echoping(1) as the "-4" or "-6" options. +DOC + _example => 4, + _re => '[46]' + }, + extraopts => { + _doc => 'Any extra options specified here will be passed unmodified to echoping(1).', + _example => '-some-letter-the-author-did-not-think-of', + }, + }); +} + 1; diff --git a/lib/probes/EchoPingChargen.pm b/lib/probes/EchoPingChargen.pm index 712953a..8f4cd9d 100644 --- a/lib/probes/EchoPingChargen.pm +++ b/lib/probes/EchoPingChargen.pm @@ -1,45 +1,43 @@ package probes::EchoPingChargen; -=head1 NAME +=head1 301 Moved Permanently -probes::EchoPingChargen - an echoping(1) probe for SmokePing - -=head1 OVERVIEW - -Measures TCP chargen (port 19) roundtrip times for SmokePing. - -=head1 SYNOPSYS - - *** Probes *** - + EchoPingChargen - - binary = /usr/bin/echoping - - *** Targets *** - - probe = EchoPingChargen - -=head1 DESCRIPTION - -Supported probe- and target-specific variables: see probes::EchoPing(3pm) +This is a Smokeping probe module. Please use the command -Note: the I<udp> variable is not supported. +C<smokeping -man probes::EchoPingChargen> -=head1 AUTHOR +to view the documentation or the command -Niko Tyni E<lt>ntyni@iki.fiE<gt> +C<smokeping -makepod probes::EchoPingChargen> -=head1 SEE ALSO - -probes::EchoPing(3pm) +to generate the POD document. =cut - use strict; use base qw(probes::EchoPing); use Carp; +sub pod_hash { + return { + name => <<DOC, +probes::EchoPingChargen - an echoping(1) probe for SmokePing +DOC + overview => <<DOC, +Measures TCP chargen (port 19) roundtrip times for SmokePing. +DOC + notes => <<DOC, +The I<udp> variable is not supported. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC, +probes::EchoPing(3pm) +DOC + } +} + sub proto_args { return ("-c"); } @@ -57,4 +55,11 @@ sub ProbeDesc($) { return "TCP Chargen pings using echoping(1)"; } +sub targetvars { + my $class = shift; + my $h = $class->SUPER::targetvars; + delete $h->{udp}; + return $h; +} + 1; diff --git a/lib/probes/EchoPingDiscard.pm b/lib/probes/EchoPingDiscard.pm index e961090..7ee7c62 100644 --- a/lib/probes/EchoPingDiscard.pm +++ b/lib/probes/EchoPingDiscard.pm @@ -1,37 +1,35 @@ package probes::EchoPingDiscard; -=head1 NAME +=head1 301 Moved Permanently -probes::EchoPingDiscard - an echoping(1) probe for SmokePing - -=head1 OVERVIEW - -Measures TCP or UDP discard (port 9) roundtrip times for SmokePing. - -=head1 SYNOPSYS - - *** Probes *** - + EchoPingDiscard - - binary = /usr/bin/echoping - - *** Targets *** +This is a Smokeping probe module. Please use the command - probe = EchoPingDiscard +C<smokeping -man probes::EchoPingDiscard> -=head1 DESCRIPTION +to view the documentation or the command -Supported probe- and target-specific variables: see probes::EchoPing(3pm) +C<smokeping -makepod probes::EchoPingDiscard> -=head1 AUTHOR +to generate the POD document. -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 SEE ALSO +=cut +sub pod_hash { + return { + name => <<DOC, +probes::EchoPingDiscard - an echoping(1) probe for SmokePing +DOC + overview => <<DOC, +Measures TCP or UDP discard (port 9) roundtrip times for SmokePing. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC, probes::EchoPing(3pm) - -=cut +DOC + } +} use strict; use base qw(probes::EchoPing); diff --git a/lib/probes/EchoPingHttp.pm b/lib/probes/EchoPingHttp.pm index 4e261b8..e2fe1c0 100644 --- a/lib/probes/EchoPingHttp.pm +++ b/lib/probes/EchoPingHttp.pm @@ -1,79 +1,16 @@ package probes::EchoPingHttp; -=head1 NAME +=head1 301 Moved Permanently -probes::EchoPingHttp - an echoping(1) probe for SmokePing - -=head1 OVERVIEW - -Measures HTTP roundtrip times (web servers and caches) for SmokePing. - -=head1 SYNOPSYS - - *** Probes *** - + EchoPingHttp - - binary = /usr/bin/echoping # mandatory - - - *** Targets *** - - probe = EchoPingHttp - - + PROBE_CONF - url = / - ignore_cache = yes - revalidate_data = no - port = 80 # default value anyway - timeout = 50 # default is 10s - -=head1 DESCRIPTION - -Supported probe-specific variables: those specified in EchoPing(3pm) -documentation. - -Supported target-specific variables: - -=over - -=item those specified in EchoPing(3pm) documentation - -except I<fill>, I<size> and I<udp>. - -=item url - -The URL to be requested from the web server or cache. Can be either relative -(/...) for web servers or absolute (http://...) for caches. - -=item port - -The TCP port to use. The default is 80. - -=item ignore_cache - -The echoping(1) "-A" option: force the proxy to ignore the cache. -Enabled if the value is anything other than 'no' or '0'. - -=item revalidate_data - -The echoping(1) "-a" option: force the proxy to revalidate data with original -server. Enabled if the value is anything other than 'no' or '0'. +This is a Smokeping probe module. Please use the command -=item timeout +C<smokeping -man probes::EchoPingHttp> -The echoping(1) "-t" option: Number of seconds to wait a reply before giving up. For TCP, -this is the maximum number of seconds for the whole connection -(setup and data exchange). +to view the documentation or the command -=back +C<smokeping -makepod probes::EchoPingHttp> -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 SEE ALSO - -EchoPing(3pm), EchoPingHttps(3pm) +to generate the POD document. =cut @@ -81,6 +18,26 @@ use strict; use base qw(probes::EchoPing); use Carp; +sub pod_hash { + return { + name => <<DOC, +probes::EchoPingHttp - an echoping(1) probe for SmokePing +DOC + overview => <<DOC, +Measures HTTP roundtrip times (web servers and caches) for SmokePing. +DOC + notes => <<DOC, +The I<fill>, I<size> and I<udp> EchoPing variables are not valid for EchoPingHttp. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC, +EchoPing(3pm), EchoPingHttps(3pm) +DOC + } +} + sub _init { my $self = shift; # HTTP doesn't fit with filling or size @@ -96,7 +53,6 @@ sub make_host { my $host = $self->SUPER::make_host($target); my $port = $target->{vars}{port}; - $port = $self->{properties}{port} unless defined $port; $host .= ":$port" if defined $port; return $host; @@ -106,21 +62,11 @@ sub proto_args { my $self = shift; my $target = shift; my $url = $target->{vars}{url}; - $url = $self->{properties}{url} unless defined $url; - $url = "/" unless defined $url; my @args = ("-h", $url); - # -t : timeout - my $timeout = $target->{vars}{timeout}; - $timeout = $self->{properties}{timeout} - unless defined $timeout; - push @args, "-t $timeout" if $timeout; - # -A : ignore cache my $ignore = $target->{vars}{ignore_cache}; - $ignore = $self->{properties}{ignore_cache} - unless defined $ignore; $ignore = 1 if (defined $ignore and $ignore ne "no" and $ignore ne "0"); @@ -128,8 +74,6 @@ sub proto_args { # -a : force cache to revalidate the data my $revalidate = $target->{vars}{revalidate_data}; - $revalidate = $self->{properties}{revalidate_data} - unless defined $revalidate; $revalidate= 1 if (defined $revalidate and $revalidate ne "no" and $revalidate ne "0"); push @args, "-a" if $revalidate and not exists $self->{_disabled}{a}; @@ -160,5 +104,40 @@ sub ProbeDesc($) { return "HTTP pings using echoping(1)"; } +sub targetvars { + my $class = shift; + my $h = $class->SUPER::targetvars; + delete $h->{udp}; + delete $h->{fill}; + delete $h->{size}; + return $class->_makevars($h, { + url => { + _doc => <<DOC, +The URL to be requested from the web server or cache. Can be either relative +(/...) for web servers or absolute (http://...) for caches. +DOC + _default => '/', + }, + port => { + _doc => 'The TCP port to use.', + _example => 80, + _re => '\d+', + }, + ignore_cache => { + _doc => <<DOC, +The echoping(1) "-A" option: force the proxy to ignore the cache. +Enabled if the value is anything other than 'no' or '0'. +DOC + _example => 'yes', + }, + revalidate_data => { + _doc => <<DOC, +The echoping(1) "-a" option: force the proxy to revalidate data with original +server. Enabled if the value is anything other than 'no' or '0'. +DOC + _example => 'no', + }, + }); +} 1; diff --git a/lib/probes/EchoPingHttps.pm b/lib/probes/EchoPingHttps.pm index 84f8b85..c860559 100644 --- a/lib/probes/EchoPingHttps.pm +++ b/lib/probes/EchoPingHttps.pm @@ -1,42 +1,16 @@ package probes::EchoPingHttps; -=head1 NAME +=head1 301 Moved Permanently -probes::EchoPingHttps - an echoping(1) probe for SmokePing - -=head1 OVERVIEW - -Measures HTTPS (HTTP over SSL) roundtrip times (web servers and caches) for -SmokePing. - -=head1 SYNOPSYS - - *** Probes *** - + EchoPingHttps - - binary = /usr/bin/echoping # mandatory - - *** Targets *** - - probe = EchoPingHttps - - + PROBE_CONF - url = / - ignore-cache = yes - force-revalidate = no - port = 443 # default value anyway +This is a Smokeping probe module. Please use the command -=head1 DESCRIPTION +C<smokeping -man probes::EchoPingHttps> -As EchoPingHttp(3pm), but SSL-enabled. - -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> +to view the documentation or the command -=head1 SEE ALSO +C<smokeping -makepod probes::EchoPingHttps> -EchoPingHttp(3pm) +to generate the POD document. =cut @@ -44,6 +18,27 @@ use strict; use base qw(probes::EchoPingHttp); use Carp; +sub pod_hash { + return { + name => <<DOC, +probes::EchoPingHttps - an echoping(1) probe for SmokePing +DOC + overview => <<DOC, +Measures HTTPS (HTTP over SSL) roundtrip times (web servers and caches) for +SmokePing. +DOC + description => <<DOC, +As EchoPingHttp(3pm), but SSL-enabled. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC, +EchoPingHttp(3pm) +DOC + } +} + sub proto_args { my $self = shift; my $target = shift; diff --git a/lib/probes/EchoPingIcp.pm b/lib/probes/EchoPingIcp.pm index 13ba896..dc93fab 100644 --- a/lib/probes/EchoPingIcp.pm +++ b/lib/probes/EchoPingIcp.pm @@ -1,56 +1,16 @@ package probes::EchoPingIcp; -=head1 NAME +=head1 301 Moved Permanently -probes::EchoPingIcp - an echoping(1) probe for SmokePing - -=head1 OVERVIEW - -Measures ICP (Internet Cache Protocol, spoken by web caches) -roundtrip times for SmokePing. - -=head1 SYNOPSYS - - *** Probes *** - + EchoPingIcp - - binary = /usr/bin/echoping # mandatory - - *** Targets *** - - probe = EchoPingHttp - - + PROBE_CONF - # this can be overridden in the targets' PROBE_CONF sections - url = / - - -=head1 DESCRIPTION - -Supported probe-specific variables: those specified in EchoPing(3pm) -documentation. - -Supported target-specific variables: - -=over +This is a Smokeping probe module. Please use the command -=item those specified in EchoPing(3pm) documentation +C<smokeping -man probes::EchoPingIcp> -except I<fill>, I<size> and I<udp>. +to view the documentation or the command -=item url +C<smokeping -makepod probes::EchoPingIcp> -The URL to be requested from the web cache. - -=back - -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 SEE ALSO - -EchoPing(3pm), EchoPingHttp(3pm) +to generate the POD document. =cut @@ -58,6 +18,27 @@ use strict; use base qw(probes::EchoPing); use Carp; +sub pod_hash { + return { + name => <<DOC, +probes::EchoPingIcp - an echoping(1) probe for SmokePing +DOC + overview => <<DOC, +Measures ICP (Internet Cache Protocol, spoken by web caches) +roundtrip times for SmokePing. +DOC + notes => <<DOC, +The I<fill>, I<size> and I<udp> EchoPing variables are not valid. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC, +EchoPing(3pm), EchoPingHttp(3pm) +DOC + } +} + sub _init { my $self = shift; # Icp doesn't fit with filling or size @@ -70,8 +51,6 @@ sub proto_args { my $self = shift; my $target = shift; my $url = $target->{vars}{url}; - $url = $self->{properties}{url} unless defined $url; - $url = "/" unless defined $url; my @args = ("-i", $url); @@ -82,7 +61,7 @@ sub test_usage { my $self = shift; my $bin = $self->{properties}{binary}; croak("Your echoping binary doesn't support ICP") - if `$bin -i/ 127.0.0.1 2>&1` =~ /not compiled|usage/i; + if `$bin -t1 -i/ 127.0.0.1 2>&1` =~ /not compiled|usage/i; $self->SUPER::test_usage; return; } @@ -91,4 +70,19 @@ sub ProbeDesc($) { return "ICP pings using echoping(1)"; } +sub targetvars { + my $class = shift; + my $h = $class->SUPER::targetvars; + delete $h->{udp}; + delete $h->{fill}; + delete $h->{size}; + return $class->_makevars($h, { + _mandatory => [ 'url' ], + url => { + _doc => "The URL to be requested from the web cache.", + _example => 'http://www.example.org/', + }, + }); +} + 1; diff --git a/lib/probes/EchoPingSmtp.pm b/lib/probes/EchoPingSmtp.pm index ef6eba0..0518758 100644 --- a/lib/probes/EchoPingSmtp.pm +++ b/lib/probes/EchoPingSmtp.pm @@ -1,38 +1,16 @@ package probes::EchoPingSmtp; -=head1 NAME +=head1 301 Moved Permanently -probes::EchoPingSmtp - an echoping(1) probe for SmokePing - -=head1 OVERVIEW - -Measures SMTP roundtrip times (mail servers) for SmokePing. - -=head1 SYNOPSYS - - *** Probes *** - + EchoPingSmtp - - binary = /usr/bin/echoping # mandatory - - *** Targets *** - probe = EchoPingSmtp - -=head1 DESCRIPTION - -Supported probe-specific variables: those specified in EchoPing(3pm) -documentation. +This is a Smokeping probe module. Please use the command -Supported target-specific variables: those specified in -EchoPing(3pm) documentation except I<fill>, I<size> and I<udp>. +C<smokeping -man probes::EchoPingSmtp> -=head1 AUTHOR +to view the documentation or the command -Niko Tyni E<lt>ntyni@iki.fiE<gt> +C<smokeping -makepod probes::EchoPingSmtp> -=head1 SEE ALSO - -EchoPing(3pm) +to generate the POD document. =cut @@ -40,6 +18,26 @@ use strict; use base qw(probes::EchoPing); use Carp; +sub pod_hash { + return { + name => <<DOC, +probes::EchoPingSmtp - an echoping(1) probe for SmokePing +DOC + overview => <<DOC, +Measures SMTP roundtrip times (mail servers) for SmokePing. +DOC + notes => <<DOC, +The I<fill>, I<size> and I<udp> EchoPing variables are not valid. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC, +EchoPing(3pm) +DOC + } +} + sub _init { my $self = shift; # SMTP doesn't fit with filling or size @@ -65,4 +63,13 @@ sub ProbeDesc($) { return "SMTP pings using echoping(1)"; } +sub targetvars { + my $class = shift; + my $h = $class->SUPER::targetvars; + delete $h->{udp}; + delete $h->{fill}; + delete $h->{size}; + return $h; +} + 1; diff --git a/lib/probes/FPing.pm b/lib/probes/FPing.pm index 9e146f6..5f92138 100644 --- a/lib/probes/FPing.pm +++ b/lib/probes/FPing.pm @@ -1,23 +1,37 @@ package probes::FPing; -=head1 NAME +=head1 301 Moved Permanently -probes::FPing - FPing Probe for SmokePing +This is a Smokeping probe module. Please use the command + +C<smokeping -man probes::FPing> -=head1 SYNOPSIS +to view the documentation or the command - *** Probes *** - + FPing - binary = /usr/sepp/bin/fping - packetsize = 1024 +C<smokeping -makepod probes::FPing> -=head1 DESCRIPTION +to generate the POD document. + +=cut -Integrates FPing as a probe into smokeping. The variable B<binary> must -point to your copy of the FPing program. If it is not installed on +use strict; +use base qw(probes::base); +use IPC::Open3; +use Symbol; +use Carp; + +sub pod_hash { + return { + name => <<DOC, +probes::FPing - FPing Probe for SmokePing +DOC + description => <<DOC, +Integrates FPing as a probe into smokeping. The variable B<binary> must +point to your copy of the FPing program. If it is not installed on your system yet, you can get it from http://www.fping.com/. + +The (optional) B<packetsize> option lets you configure the packetsize for the pings sent. -The (optional) packetsize option lets you configure the packetsize for the pings sent. The FPing manpage has the following to say on this topic: Number of bytes of ping data to send. The minimum size (normally 12) allows @@ -27,18 +41,12 @@ timestamp). The reported received data size includes the IP header 40 bytes. Default is 56, as in ping. Maximum is the theoretical maximum IP datagram size (64K), though most systems limit this to a smaller, system-dependent number. - -=head1 AUTHOR - +DOC + authors => <<'DOC', Tobias Oetiker <tobi@oetiker.ch> - -=cut - -use strict; -use base qw(probes::base); -use IPC::Open3; -use Symbol; -use Carp; +DOC + } +} sub new($$$) { @@ -48,17 +56,11 @@ sub new($$$) # no need for this if we run as a cgi unless ( $ENV{SERVER_SOFTWARE} ) { - croak "ERROR: FPing packetsize must be between 12 and 64000" - if $self->{properties}{packetsize} and - ( $self->{properties}{packetsize} < 12 or $self->{properties}{packetsize} > 64000 ); - - croak "ERROR: FPing 'binary' not defined in FPing probe definition" - unless defined $self->{properties}{binary}; - - croak "ERROR: FPing 'binary' does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; - - my $return = `$self->{properties}{binary} -C 1 localhost 2>&1`; + my $binary = join(" ", $self->binary); + my $testhost = $self->testhost; + my $return = `$binary -C 1 $testhost 2>&1`; + croak "ERROR: fping ('$binary -C 1 $testhost') could not be run: $return" + if $return =~ m/not found/; croak "ERROR: FPing must be installed setuid root or it will not work\n" if $return =~ m/only.+root/; @@ -76,10 +78,21 @@ sub new($$$) sub ProbeDesc($){ my $self = shift; - my $bytes = $self->{properties}{packetsize} || 56; + my $bytes = $self->{properties}{packetsize}||56; return "ICMP Echo Pings ($bytes Bytes)"; } +# derived class (ie. RemoteFPing) can override this +sub binary { + my $self = shift; + return $self->{properties}{binary}; +} + +# derived class (ie. FPing6) can override this +sub testhost { + return "localhost"; +} + sub ping ($){ my $self = shift; # do NOT call superclass ... the ping method MUST be overwriten @@ -91,9 +104,13 @@ sub ping ($){ return unless @{$self->addresses}; my @bytes = () ; push @bytes, "-b$self->{properties}{packetsize}" if $self->{properties}{packetsize}; + my @timeout = (); + push @timeout, "-t" . int(1000 * $self->{properties}{timeout}) if $self->{properties}{timeout}; my @cmd = ( - $self->{properties}{binary}, @bytes, - '-C', $self->pings, '-q','-B1','-i10','-r1', + $self->binary, @bytes, + '-C', $self->pings, '-q','-B1','-r1', + '-i' . $self->{properties}{mindelay}, + @timeout, @{$self->addresses}); $self->do_debug("Executing @cmd"); my $pid = open3($inh,$outh,$errh, @cmd); @@ -114,4 +131,54 @@ sub ping ($){ close $errh; } +sub probevars { + my $class = shift; + return $class->_makevars($class->SUPER::probevars, { + _mandatory => [ 'binary' ], + binary => { + _sub => sub { + my ($val) = @_; + return "ERROR: FPing 'binary' does not point to an executable" + unless -f $val and -x _; + return undef; + }, + _doc => "The location of your fping binary.", + _example => '/usr/bin/fping', + }, + packetsize => { + _re => '\d+', + _example => 5000, + _sub => sub { + my ($val) = @_; + return "ERROR: FPing packetsize must be between 12 and 64000" + if ( $val < 12 or $val > 64000 ); + return undef; + }, + _doc => "The ping packet size (in the range of 12-64000 bytes).", + + }, + timeout => { + _re => '(\d*\.)?\d+', + _example => 1.5, + _doc => <<DOC, +The fping "-t" parameter, but in (possibly fractional) seconds rather than +milliseconds, for consistency with other Smokeping probes. From fping(1): + +Initial target timeout. In the default mode, this is the amount of time that +ping waits for a response to its first request. Successive timeouts are multiplied by the backoff factor. +DOC + }, + mindelay => { + _re => '(\d*\.)?\d+', + _example => 1, + _default => 10, + _doc => <<DOC, +The fping "-i" parameter. From fping(1): + +The minimum amount of time (in milliseconds) between sending a ping packet to any target. +DOC + }, + }); +} + 1; diff --git a/lib/probes/FPing.pm.orig b/lib/probes/FPing.pm.orig deleted file mode 100644 index e71ceb0..0000000 --- a/lib/probes/FPing.pm.orig +++ /dev/null @@ -1,115 +0,0 @@ -package probes::FPing; - -=head1 NAME - -probes::FPing - FPing Probe for SmokePing - -=head1 SYNOPSIS - - *** Probes *** - + FPing - binary = /usr/sepp/bin/fping - packetsize = 1024 - -=head1 DESCRIPTION - -Integrates FPing as a probe into smokeping. The variable B<binary> must -point to your copy of the FPing program. If it is not installed on -your system yet, you can get it from http://www.fping.com/. - -The (optional) packetsize option lets you configure the packetsize for the pings sent. -The FPing manpage has the following to say on this toppic: - -Number of bytes of ping data to send. The minimum size (normally 12) allows -room for the data that fping needs to do its work (sequence number, -timestamp). The reported received data size includes the IP header -(normally 20 bytes) and ICMP header (8 bytes), so the minimum total size is -40 bytes. Default is 56, as in ping. Maximum is the theoretical maximum IP -datagram size (64K), though most systems limit this to a smaller, -system-dependent number. - -=head1 AUTHOR - -Tobias Oetiker <tobi@oetiker.ch> - -=cut - -use strict; -use base qw(probes::base); -use IPC::Open3; -use Symbol; -use Carp; - -sub new($$$) -{ - my $proto = shift; - my $class = ref($proto) || $proto; - my $self = $class->SUPER::new(@_); - - # no need for this if we run as a cgi - unless ( $ENV{SERVER_SOFTWARE} ) { - croak "ERROR: FPing packetsize must be between 12 and 64000" - if $self->{properties}{packetsize} and - ( $self->{properties}{packetsize} < 12 or $self->{properties}{packetsize} > 64000 ); - - croak "ERROR: FPing 'binary' not defined in FPing probe definition" - unless defined $self->{properties}{binary}; - - croak "ERROR: FPing 'binary' does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; - - my $return = `$self->{properties}{binary} -C 1 localhost 2>&1`; - croak "ERROR: FPing must be installed setuid root or it will not work\n" - if $return =~ m/only.+root/; - - if ($return =~ m/bytes, ([0-9.]+)\sms\s+.*\n.*\n.*:\s+([0-9.]+)/){ - $self->{pingfactor} = 1000 * $2/$1; - print "### fping seems to report in ", $1/$2, " miliseconds\n"; - } else { - $self->{pingfactor} = 1000; # Gives us a good-guess default - print "### assuming you are using an fping copy reporting in miliseconds\n"; - } - }; - - return $self; -} - -sub ProbeDesc($){ - my $self = shift; - my $bytes = $self->{properties}{packetsize} || 56; - return "ICMP Echo Pings ($bytes Bytes)"; -} - -sub ping ($){ - my $self = shift; - # do NOT call superclass ... the ping method MUST be overwriten - my %upd; - my $inh = gensym; - my $outh = gensym; - my $errh = gensym; - # pinging nothing is pointless - return unless @{$self->addresses}; - my @bytes = () ; - push @bytes, "-b$self->{properties}{packetsize}" if $self->{properties}{packetsize}; - my $pid = open3($inh,$outh,$errh, - $self->{properties}{binary}, @bytes, - '-C', $self->{cfg}{Database}{pings}, '-q','-B1','-i10','-r1', - @{$self->addresses}); - $self->{rtts}={}; - while (<$errh>){ - chomp; - next unless /^\S+\s+:\s+[\d\.]/; #filter out error messages from fping - my @times = split /\s+/; - my $ip = shift @times; - next unless ':' eq shift @times; #drop the colon - - @times = map {sprintf "%.10e", $_ / $self->{pingfactor}} sort {$a <=> $b} grep /^\d/, @times; - map { $self->{rtts}{$_} = [@times] } @{$self->{addrlookup}{$ip}} ; - } - waitpid $pid,0; - close $inh; - close $outh; - close $errh; -} - -1; diff --git a/lib/probes/FPing6.pm b/lib/probes/FPing6.pm index 7a03b48..e35e2e1 100644 --- a/lib/probes/FPing6.pm +++ b/lib/probes/FPing6.pm @@ -1,91 +1,52 @@ package probes::FPing6; -=head1 NAME +=head1 301 Moved Permanently -probes::FPing6 - FPing6 Probe for SmokePing - -=head1 SYNOPSIS +This is a Smokeping probe module. Please use the command - *** Probes *** - + FPing6 - binary = /usr/sbin/fping6 +C<smokeping -man probes::FPing6> -=head1 DESCRIPTION +to view the documentation or the command -Integrates FPing6 as a probe into smokeping. The variable B<binary> must -point to your copy of the FPing6 program. If it is not installed on -your system yet, you can get it from http://www.fping.com/. +C<smokeping -makepod probes::FPing6> -=head1 AUTHOR - -Tobias Oetiker <tobi@oetiker.ch> +to generate the POD document. =cut use strict; -use base qw(probes::base); -use IPC::Open3; -use Symbol; -use Carp; - -sub new($$$) -{ - my $proto = shift; - my $class = ref($proto) || $proto; - my $self = $class->SUPER::new(@_); +use base qw(probes::FPing); - croak "ERROR: FPing6 'binary' not defined in FPing6 probe definition" - unless defined $self->{properties}{binary}; +sub pod_hash { + return { + name => <<DOC, +probes::FPing6 - FPing6 Probe for SmokePing +DOC + description => <<DOC, +Integrates FPing6 as a probe into smokeping. This probe is derived from +FPing; the only difference is that the target host used for checking +the fping command output is ::1 instead of localhost. +DOC + authors => <<'DOC', +Tobias Oetiker <tobi@oetiker.ch> - croak "ERROR: FPing6 'binary' does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; - - $_ = `$self->{properties}{binary} -C 1 localhost 2>&1`; - croak "ERROR: FPing6 must be installed setuid root or it will not work\n" if m/only.+root/; - if (m/bytes, ([0-9.]+)\sms\s+.*\n.*\n.*:\s+([0-9.]+)/){ - $self->{pingfactor} = 1000 * $2/$1; - print "### fping6 seems to report in ", $1/$2, " miliseconds\n" unless $ENV{SERVER_SOFTWARE}; - } else { - $self->{pingfactor} = 1000; # Gives us a good-guess default - print "### assuming you are using an fping6 copy reporting in miliseconds\n" unless $ENV{SERVER_SOFTWARE}; - }; - return $self; +Niko Tyni <ntyni@iki.fi> +DOC + see_also => <<DOC +probes::FPing +DOC + } } -sub ProbeDesc($){ - return "IPv6-ICMP Echo Pings"; +sub testhost { + return "::1"; } -sub ping ($){ - my $self = shift; - # do NOT call superclass ... the ping method MUST be overwriten - my %upd; - my $inh = gensym; - my $outh = gensym; - my $errh = gensym; - # pinging nothing is pointless - return unless @{$self->addresses}; - my @cmd = ( - $self->{properties}{binary}, - '-C', $self->pings, '-q', - @{$self->addresses}); - $self->do_debug("Executing @cmd"); - my $pid = open3($inh,$outh,$errh, @cmd); - $self->{rtts}={}; - while (<$errh>){ - chomp; - next unless /^\S+\s+:\s+[\d\.]/; #filter out error messages from fping - my @times = split /\s+/; - my $ip = shift @times; - next unless ':' eq shift @times; #drop the colon - - @times = map {sprintf "%.10e", $_ / $self->{pingfactor}} sort {$a <=> $b} grep {$_ ne "-"} @times; - map { $self->{rtts}{$_} = [@times] } @{$self->{addrlookup}{$ip}} ; - } - waitpid $pid,0; - close $inh; - close $outh; - close $errh; +sub probevars { + my $self = shift; + my $h = $self->SUPER::probevars; + $h->{binary}{_example} = "/usr/bin/fping6"; + return $h; } - + 1; diff --git a/lib/probes/IOSPing.pm b/lib/probes/IOSPing.pm index 3b71148..d73d046 100644 --- a/lib/probes/IOSPing.pm +++ b/lib/probes/IOSPing.pm @@ -1,62 +1,38 @@ package probes::IOSPing; -=head1 NAME +=head1 301 Moved Permanently -probes::IOSPing - Cisco IOS Probe for SmokePing - -=head1 SYNOPSIS - - *** Probes *** - + IOSPing - binary = /usr/bin/remsh - packetsize = 1024 - forks = 1 - - ++ PROBE_CONF - ioshost = router - iosuser = user - iosint = source_address - -=head1 DESCRIPTION - -Integrates Cisco IOS as a probe into smokeping. Uses the rsh / remsh -protocol to run a ping from an IOS device. - -=head1 OPTIONS - -The binary and ioshost options are mandatory. +This is a Smokeping probe module. Please use the command -The binary option specifies the path of the binary to be used to -connect to the IOS device. Commonly used binaries are /usr/bin/rsh -and /usr/bin/remsh, although any script or binary should work if can -be called as - - /path/to/binary [ -l user ] router ping +C<smokeping -man probes::IOSPing> -to produce the IOS ping dialog on stdin & stdout. +to view the documentation or the command -The (optional) packetsize option lets you configure the packetsize for -the pings sent. +C<smokeping -makepod probes::IOSPing> -The (optional) forks options lets you configure the number of -simultaneous remote pings to be run. NB Some IOS devices have a -maximum of 5 VTYs available, so be careful not to hit a limit. +to generate the POD document. -The ioshost option specifies the IOS device which should be used for -the ping. +=cut -The (optional) iosuser option allows you to specify the remote -username the IOS device. If this option is omitted, the username -defaults to the default user used by the remsh command (usually the -user running the remsh command, ie the user running SmokePing). +use strict; +use base qw(probes::basefork); +use IPC::Open2; +use Symbol; +use Carp; -The (optional) iosint option allows you to specify the source address -or interface in the IOS device. The value should be an IP address or -an interface name such as "Ethernet 1/0". If this option is omitted, -the IOS device will pick the IP address of the outbound interface to -use. +my $e = "="; -=head1 IOS CONFIGURATION +sub pod_hash { + return { + name => <<DOC, +probes::IOSPing - Cisco IOS Probe for SmokePing +DOC + description => <<DOC, +Integrates Cisco IOS as a probe into smokeping. Uses the rsh / remsh +protocol to run a ping from an IOS device. +DOC + notes => <<DOC, +=head2 IOS Configuration The IOS device must have rsh enabled and an appropriate trust defined, eg: @@ -66,14 +42,15 @@ eg: ip rcmd remote-host smoke 192.168.1.2 smoke enable ! -=head1 NOTES +Some IOS devices have a maximum of 5 VTYs available, so be careful not to +hit a limit with the 'forks' variable. -=head2 Password authentication +${e}head2 Password authentication It is not possible to use password authentication with rsh or remsh due to fundamental limitations of the protocol. -=head2 Ping packet size +${e}head2 Ping packet size The FPing manpage has the following to say on the topic of ping packet size: @@ -85,22 +62,16 @@ header (normally 20 bytes) and ICMP header (8 bytes), so the minimum total size is 40 bytes. Default is 56, as in ping. Maximum is the theoretical maximum IP datagram size (64K), though most systems limit this to a smaller, system-dependent number. - -=head1 AUTHOR - +DOC + authors => <<'DOC', Paul J Murphy <paul@murph.org> based on probes::FPing by Tobias Oetiker <tobi@oetiker.ch> - -=cut - -use strict; -use base qw(probes::basefork); -use IPC::Open2; -use Symbol; -use Carp; +DOC + } +} sub new($$$) { @@ -110,16 +81,6 @@ sub new($$$) # no need for this if we run as a cgi unless ( $ENV{SERVER_SOFTWARE} ) { - croak "ERROR: IOSPing packetsize must be between 12 and 64000" - if $self->{properties}{packetsize} and - ( $self->{properties}{packetsize} < 12 or $self->{properties}{packetsize} > 64000 ); - - croak "ERROR: IOSPing 'binary' not defined in IOSPing probe definition" - unless defined $self->{properties}{binary}; - - croak "ERROR: IOSPing 'binary' does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; - $self->{pingfactor} = 1000; # Gives us a good-guess default print "### assuming you are using an IOS reporting in miliseconds\n"; }; @@ -129,14 +90,14 @@ sub new($$$) sub ProbeDesc($){ my $self = shift; - my $bytes = $self->{properties}{packetsize} || 56; + my $bytes = $self->{properties}{packetsize}; return "Cisco IOS - ICMP Echo Pings ($bytes Bytes)"; } sub pingone ($$){ my $self = shift; my $target = shift; - my $bytes = $self->{properties}{packetsize} || 56; + my $bytes = $self->{properties}{packetsize}; # do NOT call superclass ... the ping method MUST be overwriten my %upd; my $inh = gensym; @@ -144,9 +105,6 @@ sub pingone ($$){ my @args = (); my $pings = $self->pings($target); - croak "ERROR: IOSPing 'ioshost' not defined" - unless defined $target->{vars}{ioshost}; - push(@args,$self->{properties}{binary}); push(@args,'-l',$target->{vars}{iosuser}) if defined $target->{vars}{iosuser}; @@ -229,4 +187,76 @@ sub pingone ($$){ return @times; } +sub probevars { + my $class = shift; + return $class->_makevars($class->SUPER::probevars, { + _mandatory => ['binary'], + binary => { + _doc => <<DOC, +The binary option specifies the path of the binary to be used to +connect to the IOS device. Commonly used binaries are /usr/bin/rsh +and /usr/bin/remsh, although any script or binary should work if can +be called as + + /path/to/binary [ -l user ] router ping + +to produce the IOS ping dialog on stdin & stdout. +DOC + _example => '/usr/bin/rsh', + _sub => sub { + my $val = shift; + -x $val or return "ERROR: binary '$val' is not executable"; + return undef; + }, + }, + packetsize => { + _doc => <<DOC, +The (optional) packetsize option lets you configure the packetsize for +the pings sent. +DOC + _default => 56, + _re => '\d+', + _sub => sub { + my $val = shift; + return "ERROR: packetsize must be between 12 and 64000" + unless $val >= 12 and $val <= 64000; + return undef; + }, + }, + }); +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + _mandatory => [ 'ioshost' ], + ioshost => { + _doc => <<DOC, +The ioshost option specifies the IOS device which should be used for +the ping. +DOC + _example => 'my.cisco.router', + }, + iosuser => { + _doc => <<DOC, +The (optional) iosuser option allows you to specify the remote +username the IOS device. If this option is omitted, the username +defaults to the default user used by the remsh command (usually the +user running the remsh command, ie the user running SmokePing). +DOC + _example => 'admin', + }, + iosint => { + _doc => <<DOC, +The (optional) iosint option allows you to specify the source address +or interface in the IOS device. The value should be an IP address or +an interface name such as "Ethernet 1/0". If this option is omitted, +the IOS device will pick the IP address of the outbound interface to +use. +DOC + _example => 'Ethernet 1/0', + }, + }); +} + 1; diff --git a/lib/probes/LDAP.pm b/lib/probes/LDAP.pm index b7e5342..2bb966f 100644 --- a/lib/probes/LDAP.pm +++ b/lib/probes/LDAP.pm @@ -1,41 +1,37 @@ package probes::LDAP; -=head1 NAME +=head1 301 Moved Permanently -probes::LDAP - a LDAP probe for SmokePing - -=head1 OVERVIEW +This is a Smokeping probe module. Please use the command -Measures LDAP search latency for SmkoePing +C<smokeping -man probes::LDAP> -=head1 SYNOPSYS +to view the documentation or the command - *** Probes *** - + LDAP +C<smokeping -makepod probes::LDAP> - passwordfile = /usr/share/smokeping/etc/password # optional - sleeptime = 0.5 # optional, 1 second by default +to generate the POD document. - *** Targets *** +=cut - probe = LDAP +use strict; +use probes::passwordchecker; +use Net::LDAP; +use Time::HiRes qw(gettimeofday sleep); +use base qw(probes::passwordchecker); +use IO::Socket::SSL; - + PROBE_CONF - port = 389 # optional - version = 3 # optional - start_tls = 1 # disabled by default - timeout = 60 # optional - - base = dc=foo,dc=bar # optional - filter = uid=testuser # the actual search - attrs = uid,someotherattr - - # if binddn isn't present, the LDAP bind is unauthenticated - binddn = uid=testuser,dc=foo,dc=bar - password = mypass # if not present in <passwordfile> - -=head1 DESCRIPTION +my $DEFAULTINTERVAL = 1; +sub pod_hash { + return { + name => <<DOC, +probes::LDAP - a LDAP probe for SmokePing +DOC + overview => <<DOC, +Measures LDAP search latency for SmokePing +DOC + description => <<DOC, This probe measures LDAP query latency for SmokePing. The query is specified by the target-specific variable `filter' and, optionally, by the target-specific variable `base'. The attributes @@ -54,57 +50,96 @@ The location of this file is given in the probe-specific variable of this file (summary: colon-separated triplets of the form `<host>:<bind-dn>:<password>') -The probe tries to be nice to the server and sleeps for the probe-specific -variable `sleeptime' (one second by default) between each authentication -request. - -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 BUGS - +The probe tries to be nice to the server and does not send authentication +requests more frequently than once every X seconds, where X is the value +of the target-specific "min_interval" variable ($DEFAULTINTERVAL by default). +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + bugs => <<DOC, There should be a way of specifying TLS options, such as the certificates involved etc. The probe has an ugly way of working around the fact that the IO::Socket::SSL class complains if start_tls() is done more than once in the same program. But It Works For Me (tm). - -=cut - -use strict; -use probes::passwordchecker; -use Net::LDAP; -use Time::HiRes qw(gettimeofday sleep); -use base qw(probes::passwordchecker); -use IO::Socket::SSL; +DOC + } +} sub ProbeDesc { return "LDAP queries"; } +sub probevars { + my $class = shift; + my $h = $class->SUPER::probevars; + delete $h->{timeout}; + return $h; +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + _mandatory => [ 'filter' ], + port => { + _re => '\d+', + _doc => "TCP port of the LDAP server", + _example => 389, + }, + version => { + _re => '\d+', + _doc => "The LDAP version to be used.", + _example => 3, + }, + start_tls => { + _doc => "If true, encrypt the connection with the starttls command. Disabled by default.", + _example => "1", + }, + timeout => { + _doc => "LDAP query timeout in seconds.", + _re => '\d+', + _example => 10, + _default => 5, + }, + base => { + _doc => "The base to be used in the LDAP query", + _example => "dc=foo,dc=bar", + }, + filter => { + _doc => "The actual search to be made", + _example => "uid=testuser", + }, + attrs => { + _doc => "The attributes queried.", + _example => "uid,someotherattr", + }, + binddn => { + _doc => "If present, authenticate the LDAP bind with this DN.", + _example => "uid=testuser,dc=foo,dc=bar", + }, + password => { + _doc => "The password to be used, if not present in <passwordfile>.", + _example => "mypass", + }, + mininterval => { + _default => $DEFAULTINTERVAL, + _doc => "The minimum interval between each query sent, in (possibly fractional) second +s.", + _re => '(\d*\.)?\d+', + }, + }); +} + sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = $class->SUPER::new(@_); - my $sleeptime = $self->{properties}{sleeptime}; - $sleeptime = 1 unless defined $sleeptime; - $self->sleeptime($sleeptime); - return $self; } -sub sleeptime { - my $self = shift; - my $newval = shift; - - $self->{sleeptime} = $newval if defined $newval; - return $self->{sleeptime}; -} - - sub pingone { my $self = shift; my $target = shift; @@ -114,11 +149,21 @@ sub pingone { my $version = $vars->{version} || 3; my $port = $vars->{port}; + my $mininterval = $vars->{mininterval}; + my $binddn = $vars->{binddn}; my $timeout = $vars->{timeout}; - my $password = $vars->{password} || $self->password($host, $binddn) if defined $binddn; + my $password; + if (defined $binddn) { + $password = $self->password($host, $binddn); + if (defined $vars->{password} and + $vars->{password} ne ($self->{properties}{password}||"")) { + $password = $vars->{password}; + } + $password ||= $self->{properties}{password}; + } my $start_tls = $vars->{start_tls}; @@ -128,14 +173,20 @@ sub pingone { my $attrs = $vars->{attrs}; - my @attrs = split(/,/, $attrs); + my @attrs = split(/,/, $attrs||""); + my $attrsref = @attrs ? \@attrs : undef; my @times; + my $start; for (1..$self->pings($target)) { + if (defined $start) { + my $elapsed = gettimeofday() - $start; + my $timeleft = $mininterval - $elapsed; + sleep $timeleft if $timeleft > 0; + } local $IO::Socket::SSL::SSL_Context_obj; # ugly but necessary - sleep $self->sleeptime unless $_ == 1; # be nice - my $start = gettimeofday(); + $start = gettimeofday(); my $ldap = new Net::LDAP($host, port => $port, version => $version, timeout => $timeout) or do { $self->do_log("connection error on $host: $!"); @@ -163,7 +214,7 @@ sub pingone { $ldap->unbind; next; }; - $mesg = $ldap->search(base => $base, filter => $filter, attrs => [ @attrs ]); + $mesg = $ldap->search(base => $base, filter => $filter, attrs => $attrsref); $mesg->code and do { $self->do_log("filter error on $host: " . $mesg->error); $ldap->unbind; diff --git a/lib/probes/Radius.pm b/lib/probes/Radius.pm index 2c4fb96..6d0705b 100644 --- a/lib/probes/Radius.pm +++ b/lib/probes/Radius.pm @@ -1,55 +1,54 @@ package probes::Radius; -=head1 NAME +=head1 301 Moved Permanently -probes::Radius - a RADIUS authentication probe for SmokePing - -=head1 OVERVIEW - -Measures RADIUS authentication latency for SmokePing +This is a Smokeping probe module. Please use the command -=head1 SYNOPSYS +C<smokeping -man probes::Radius> - *** Probes *** - + Radius +to view the documentation or the command - passwordfile = /usr/share/smokeping/etc/password - secretfile = /etc/raddb/secret - sleeptime = 0.5 # optional, 1 second by default - username = test-user # optional, overridden by target - password = test-password # optional, overridden by target - secret = test-secret # optional, overridden by target +C<smokeping -makepod probes::Radius> - *** Targets *** +to generate the POD document. - probe = Radius +=cut - + PROBE_CONF - username = testuser - secret = myRadiusSecret # if not present in <secretfile> - password = testuserPass # if not present in <passwordfile> - port = 1645 # optional - nas_ip_address = 1.2.3.4 # optional +use strict; +use base qw(probes::passwordchecker); +use Authen::Radius; +use Time::HiRes qw(gettimeofday sleep); +use Carp; -=head1 DESCRIPTION +my $DEFAULTINTERVAL = 1; +sub pod_hash { + return { + name => <<DOC, +probes::Radius - a RADIUS authentication probe for SmokePing +DOC + overview => <<DOC, +Measures RADIUS authentication latency for SmokePing +DOC + description => <<DOC, This probe measures RADIUS (RFC 2865) authentication latency for SmokePing. The username to be tested is specified in either the probe-specific or the target-specific variable `username', with the target-specific one overriding the probe-specific one. -The password can be specified either (in order of precedence, with the latter -overriding the former) in the probe-specific variable `password', in the -target-specific variable `password' or in an external file. The location of -this file is given in the probe-specific variable `passwordfile'. See -probes::passwordchecker(3pm) for the format of this file (summary: -colon-separated triplets of the form `<host>:<username>:<password>') +The password can be specified either (in order of precedence, with +the latter overriding the former) in the probe-specific variable +`password', in an external file or in the target-specific variable +`password'. The location of this file is given in the probe-specific +variable `passwordfile'. See probes::passwordchecker(3pm) for the +format of this file (summary: colon-separated triplets of the form +`<host>:<username>:<password>') The RADIUS protocol requires a shared secret between the server and the client. This secret can be specified either (in order of precedence, with the latter -overriding the former) in the probe-specific variable `secret', in the -target-specific variable `secret' or in an external file. +overriding the former) in the probe-specific variable `secret', in an external file +or in the target-specific variable `secret'. This external file is located by the probe-specific variable `secretfile', and it should contain whitespace-separated pairs of the form `<host> <secret>'. Comments and blank lines are OK. @@ -58,26 +57,18 @@ If the optional probe-specific variable `nas_ip_address' is specified, its value is inserted into the authentication requests as the `NAS-IP-Address' RADIUS attribute. -The probe tries to be nice to the server and sleeps for the probe-specific -variable `sleeptime' (one second by default) between each authentication -request. - -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 BUGS - +The probe tries to be nice to the server and does not send authentication +requests more frequently than once every X seconds, where X is the value +of the target-specific "min_interval" variable ($DEFAULTINTERVAL by default). +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + bugs => <<DOC, There should be a more general way of specifying RADIUS attributes. - -=cut - -use strict; -use probes::passwordchecker; -use base qw(probes::passwordchecker); -use Authen::Radius; -use Time::HiRes qw(gettimeofday sleep); -use Carp; +DOC + } +} sub ProbeDesc { return "RADIUS queries"; @@ -91,6 +82,10 @@ sub new { # no need for this if we run as a cgi unless ($ENV{SERVER_SOFTWARE}) { if (defined $self->{properties}{secretfile}) { + my @stat = stat($self->{properties}{secretfile}); + my $mode = $stat[2]; + carp("Warning: secret file $self->{properties}{secretfile} is world-readable\n") + if defined $mode and $mode & 04; open(S, "<$self->{properties}{secretfile}") or croak("Error opening specified secret file $self->{properties}{secretfile}: $!"); while (<S>) { @@ -105,10 +100,6 @@ sub new { close S; } - my $sleeptime = $self->{properties}{sleeptime}; - $sleeptime = 1 unless defined $sleeptime; - $self->sleeptime($sleeptime); - } return $self; @@ -123,21 +114,21 @@ sub secret { return $self->{secret}{$host}; } -sub sleeptime { - my $self = shift; - my $newval = shift; - - $self->{sleeptime} = $newval if defined $newval; - return $self->{sleeptime}; -} - sub pingone { my $self = shift; my $target = shift; my $host = $target->{addr}; my $vars = $target->{vars}; - my $username = $vars->{username} || $self->{properties}->{username}; - my $secret = $vars->{secret} || $self->secret($host) || $self->{properties}->{secret}; + my $mininterval = $vars->{mininterval}; + my $username = $vars->{username}; + my $secret = $self->secret($host); + if (defined $vars->{secret} and + $vars->{secret} ne ($self->{properties}{secret}||"")) { + $secret = $vars->{secret}; + } + $secret ||= $self->{properties}{secret}; + + my $timeout = $vars->{timeout}; $self->do_log("Missing RADIUS secret for $host"), return unless defined $secret; @@ -145,17 +136,27 @@ sub pingone { $self->do_log("Missing RADIUS username for $host"), return unless defined $username; - my $password = $vars->{password} || $self->password($host, $username) || $self->{properties}->{password}; - - my $port = $vars->{port}; - $host .= ":$port" if defined $port; + my $password = $self->password($host, $username); + if (defined $vars->{password} and + $vars->{password} ne ($self->{properties}{password}||"")) { + $password = $vars->{password}; + } + $password ||= $self->{properties}{password}; $self->do_log("Missing RADIUS password for $host/$username"), return unless defined $password; + my $port = $vars->{port}; + $host .= ":$port" if defined $port; + my @times; + my $elapsed; for (1..$self->pings($target)) { - my $r = new Authen::Radius(Host => $host, Secret => $secret); + if (defined $elapsed) { + my $timeleft = $mininterval - $elapsed; + sleep $timeleft if $timeleft > 0; + } + my $r = new Authen::Radius(Host => $host, Secret => $secret, TimeOut => $timeout); $r->add_attributes( { Name => 1, Value => $username, Type => 'string' }, { Name => 2, Value => $password, Type => 'string' }, @@ -172,13 +173,76 @@ sub pingone { $result = "OK" if $c == ACCESS_ACCEPT; $result = "fail" if $c == ACCESS_REJECT; } else { - $result = "no reply"; + if (defined $r->get_error) { + $result = "error: " . $r->strerror; + } else { + $result = "no reply"; + } } - $self->do_debug("$host: radius query $_: $result, " . ($end - $start)); - push @times, $end - $start if (defined $c and $c == ACCESS_ACCEPT); - sleep $self->sleeptime; # be nice + $elapsed = $end - $start; + $self->do_debug("$host: radius query $_: $result, $elapsed"); + push @times, $elapsed if (defined $c and $c == ACCESS_ACCEPT); } return sort { $a <=> $b } @times; } +sub probevars { + my $class = shift; + my $h = $class->SUPER::probevars; + delete $h->{timeout}; + return $class->_makevars($h, { + secretfile => { + _doc => <<DOC, +A file containing the RADIUS shared secrets for the targets. It should contain +whitespace-separated pairs of the form `<host> <secret>'. Comments and blank lines +are OK. +DOC + _example => '/another/place/secret', + _sub => sub { + my $val = shift; + -r $val or return "ERROR: secret file $val is not readable."; + return undef; + }, + }, + }); +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + _mandatory => [ 'username' ], + username => { + _doc => 'The username to be tested.', + _example => 'test-user', + }, + password => { + _doc => 'The password for the user, if not present in the password file.', + _example => 'test-password', + }, + secret => { + _doc => 'The RADIUS shared secret for the target, if not present in the secrets file.', + _example => 'test-secret', + }, + nas_ip_address => { + _doc => 'The NAS-IP-Address RADIUS attribute for the authentication requests. Not needed everywhere.', + _example => '10.1.2.3', + }, + mininterval => { + _default => $DEFAULTINTERVAL, + _doc => "The minimum interval between each authentication request sent, in (possibly fractional) seconds.", + _re => '(\d*\.)?\d+', + }, + timeout => { + _default => 5, + _doc => "Timeout in seconds for the RADIUS queries.", + _re => '\d+', + }, + port => { + _doc => 'The RADIUS port to be used', + _re => '\d+', + _example => 1645, + }, + }); +} + 1; diff --git a/lib/probes/RemoteFPing.pm b/lib/probes/RemoteFPing.pm index da87b6c..9f26fd3 100644 --- a/lib/probes/RemoteFPing.pm +++ b/lib/probes/RemoteFPing.pm @@ -1,57 +1,31 @@ package probes::RemoteFPing; -=head1 NAME +=head1 301 Moved Permanently -probes::RemoteFPing - Remote FPing Probe for SmokePing +This is a Smokeping probe module. Please use the command -=head1 SYNOPSIS +C<smokeping -man probes::RemoteFPing> - *** Probes *** - + RemoteFPing - binary = /usr/bin/ssh - packetsize = 1024 - rhost = HostA.foobar.com - ruser = foo - rbinary = /usr/local/sbin/fping +to view the documentation or the command - *** Targets *** - + Targetname - Probe = RemoteFPing - Menu = menuname - Title = Remote Fping from HostA to HostB - Host = HostB.barfoo.com +C<smokeping -makepod probes::RemoteFPing> +to generate the POD document. -=head1 DESCRIPTION +=cut +sub pod_hash { + return { + name => <<DOC, +probes::RemoteFPing - Remote FPing Probe for SmokePing +DOC + description => <<DOC, Integrates the remote execution of FPing via ssh/rsh into smokeping. The variable B<binary> must point to your copy of the ssh/rsh program. - -=head1 OPTIONS - -The B<binary> and B<rhost> are mandatory. The B<binary> option -specifies the path of the remote shell program (usually ssh, -rsh or remsh). Any other script or binary that can be called as - - binary [ -l ruser ] rhost rbinary - -may be used. - -The (optional) B<packetsize> option lets you configure the packetsize -for the pings sent. - -The B<rhost> option specifies the remote device from where fping will -be launched. - -The (optional) B<ruser> option allows you to specify the remote user, -if different from the one running the smokeping daemon. - -The (optional) B<rbinary> option allows you to specify the location of -the remote fping binary. If not specified the probe will assume that -fping is in the remote host's path. - -=head1 NOTES - +The variable B<rbinary> must point to your copy of the fping program +at the remote end. +DOC + notes => <<'DOC', It is important to make sure that you can access the remote machine without a password prompt, otherwise this probe will not work properly. To test just try something like this: @@ -61,104 +35,88 @@ To test just try something like this: The next thing you see must be fping's output. The B<rhost>, B<ruser> and B<rbinary> variables used to be configured in -the PROBE_CONF section of the first target or its parents They were moved +the Targets section of the first target or its parents They were moved to the Probes section, because the variables aren't really target-specific -(all the targets are measured with the same parameters). The PROBE_CONF +(all the targets are measured with the same parameters). The Targets sections aren't recognized anymore. +DOC + authors => <<'DOC', + Luis F Balbinot <hades@inf.ufrgs.br> -=head1 AUTHOR + Niko Tyni <ntyni@iki.fi> -Luis F Balbinot <hades@inf.ufrgs.br> + derived from probes::FPing by -based on probes::FPing by - -Tobias Oetiker <tobi@oetiker.ch> - -=cut + Tobias Oetiker <tobi@oetiker.ch> +DOC + bugs => <<DOC +This functionality should be in a generic 'remote execution' module +so that it could be used for the other probes too. +DOC + } +} use strict; -use base qw(probes::base); -use IPC::Open3; -use Symbol; -use Carp; - -sub new($$$) { - my $proto = shift; - my $class = ref($proto) || $proto; - my $self = $class->SUPER::new(@_); - - # no need for this if we run as a cgi - unless ( $ENV{SERVER_SOFTWARE} ) { - croak "ERROR: RemoteFPing packetsize must be between 12 and 64000" - if $self->{properties}{packetsize} and - ( $self->{properties}{packetsize} < 12 or $self->{properties}{packetsize} > 64000 ); - - croak "ERROR: RemoteFPing 'binary' not defined in RemoteFPing probe definition" - unless defined $self->{properties}{binary}; - - croak "ERROR: RemoteFPing 'binary' does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; - - croak "ERROR: RemoteFPing 'rhost' not defined in RemoteFPing probe definition. This might be because the configuration syntax has changed. See the RemoteFPing manual for details." - unless defined $self->{properties}{rhost}; - - $self->{pingfactor} = 1000; # Gives us a good-guess default - print "### assuming you are using a remote fping copy reporting in milliseconds\n"; - }; - - return $self; -} +use base qw(probes::FPing); sub ProbeDesc($) { my $self = shift; - my $bytes = $self->{properties}{packetsize} || 56; - return "Remote ICMP Echo Pings ($bytes Bytes)"; + my $superdesc = $self->SUPER::ProbeDesc; + return "Remote $superdesc"; } -sub ping ($) { +sub binary { my $self = shift; - - # do NOT call superclass ... the ping method MUST be overwriten - my %upd; - my $inh = gensym; - my $outh = gensym; - my $errh = gensym; - # pinging nothing is pointless - return unless @{$self->addresses}; - my @bytes = (); - - push @bytes, "-b$self->{properties}{packetsize}" if $self->{properties}{packetsize}; - - my @rargs; + my @ret = ( $self->SUPER::binary ); for my $what (qw(ruser rhost rbinary)) { - my $prefix = ($what eq 'ruser' ? "-l" : ""); - if (defined $self->{properties}{$what}) { - push @rargs, $prefix . $self->{properties}{$what}; - } + my $prefix = ($what eq 'ruser' ? "-l" : ""); + if (defined $self->{properties}{$what}) { + push @ret, $prefix . $self->{properties}{$what}; + } } + return @ret; +} - my $query = "$self->{properties}{binary} @rargs @bytes -C " . $self->pings . " -q -B1 -i10 -r1 @{$self->addresses}"; - - $self->do_debug("query=$query\n"); - - my $pid = open3($inh,$outh,$errh,$query ); - my @times =() ; - $self->{rtts}={}; - while (<$errh>) { - chomp; - next unless /^\S+\s+:\s+[\d\.]/; #filter out error messages from fping - $self->do_debug("array element=$_ \n"); - @times = split /\s+/; - my $ip = shift @times; - next unless ':' eq shift @times; #drop the colon - @times = map {sprintf "%.10e", $_ / $self->{pingfactor}} sort {$a <=> $b} grep {$_ ne "-"} @times; - map { $self->{rtts}{$_} = [@times] } @{$self->{addrlookup}{$ip}} ; - } - waitpid $pid,0; - close $inh; - close $outh; - close $errh; - return @times; +sub probevars { + my $class = shift; + my $h = $class->SUPER::probevars; + $h->{rbinary} = $h->{binary}; + delete $h->{binary}; + delete $h->{rbinary}{sub}; # we can't check the remote program's -x bit + @{$h->{_mandatory}} = map { $_ ne 'binary' ? $_ : 'rbinary' } @{$h->{_mandatory}}; + return $class->_makevars($h, { + _mandatory => [ 'binary', 'rhost' ], + binary => { + _doc => <<DOC, +This variable specifies the path of the remote shell program (usually ssh, +rsh or remsh). Any other script or binary that can be called as + +binary [ -l ruser ] rhost rbinary + +may be used. +DOC + _example => '/usr/bin/ssh', + _sub => sub { + my $val = shift; + -x $val or return "ERROR: binary '$val' is not executable"; + return undef; + }, + }, + rhost => { + _doc => <<DOC, +The B<rhost> option specifies the remote device from where fping will +be launched. +DOC + _example => 'my.pinger.host', + }, + ruser => { + _doc => <<DOC, +The (optional) B<ruser> option allows you to specify the remote user, +if different from the one running the smokeping daemon. +DOC + _example => 'foo', + }, + }); } 1; diff --git a/lib/probes/SSH.pm b/lib/probes/SSH.pm index ede9c4c..f91a8d0 100644 --- a/lib/probes/SSH.pm +++ b/lib/probes/SSH.pm @@ -1,59 +1,16 @@ package probes::SSH; -=head1 NAME +=head1 301 Moved Permanently -probes::SSH - Secure Shell Probe for SmokePing - -=head1 SYNOPSIS - - *** Probes *** - + SSH - binary = /usr/bin/ssh-keyscan - - *** Targets *** - probe = SSH - forks = 10 - - + First - menu = First - title = First Target - # .... - -=head1 DESCRIPTION - -Integrates ssh-keyscan as a probe into smokeping. The variable B<binary> must -point to your copy of the ssh-keyscan program. If it is not installed on -your system yet, you should install openssh >= 3.8p1 - -The Probe asks the given host n-times for it's public key. Where n is -the amount specified in the config File. - -Supported probe-specific variables: - -=over - -=item binary - -The location of your ssh-keyscan binary. - -=item forks +This is a Smokeping probe module. Please use the command -The number of concurrent processes to be run. See probes::basefork(3pm) -for details. +C<smokeping -man probes::SSH> -=back +to view the documentation or the command -Supported target-level probe variables: - -=over - -=back - - -=head1 AUTHOR - -Christian Recktenwald<lt>smokeping-contact@citecs.de<gt> +C<smokeping -makepod probes::SSH> +to generate the POD document. =cut @@ -64,6 +21,25 @@ use Symbol; use Carp; use POSIX; +sub pod_hash { + return { + name => <<DOC, +probes::SSH - Secure Shell Probe for SmokePing +DOC + description => <<DOC, +Integrates ssh-keyscan as a probe into smokeping. The variable B<binary> must +point to your copy of the ssh-keyscan program. If it is not installed on +your system yet, you should install openssh >= 3.8p1 + +The Probe asks the given host n-times for it's public key. Where n is +the amount specified in the config File. +DOC + authors => <<'DOC', +Christian Recktenwald <smokeping-contact@citecs.de> +DOC + } +} + my $ssh_re=qr/^# \S+ SSH-/i; sub new($$$) @@ -75,11 +51,6 @@ sub new($$$) # no need for this if we run as a cgi unless ( $ENV{SERVER_SOFTWARE} ) { - croak "ERROR: SSH 'binary' not defined in SSH probe definition" - unless defined $self->{properties}{binary}; - - croak "ERROR: SSH 'binary' does not point to an executable" - unless -f $self->{properties}{binary} and -x $self->{properties}{binary}; my $call = "$self->{properties}{binary} -t rsa localhost"; my $return = `$call 2>&1`; if ($return =~ m/$ssh_re/s){ @@ -134,4 +105,20 @@ sub pingone ($){ return @times; } +sub probevars { + my $class = shift; + return $class->_makevars($class->SUPER::probevars, { + _mandatory => [ 'binary' ], + binary => { + _doc => "The location of your ssh-keyscan binary.", + _example => '/usr/bin/ssh-keyscan', + _sub => sub { + my $val = shift; + -x $val or return "ERROR: binary '$val' is not executable"; + return undef; + }, + }, + }) +} + 1; diff --git a/lib/probes/base.pm b/lib/probes/base.pm index 79165f1..3101e01 100644 --- a/lib/probes/base.pm +++ b/lib/probes/base.pm @@ -1,17 +1,16 @@ package probes::base; -=head1 NAME +=head1 301 Moved Permanently -probes::base - Base Class for implementing SmokePing Probes +This is a Smokeping probe module. Please use the command -=head1 OVERVIEW - -For the time being, please use the probes::FPing for -inspiration when implementing your own probes. +C<smokeping -man probes::base> -=head1 AUTHOR +to view the documentation or the command -Tobias Oetiker <tobi@oetiker.ch> +C<smokeping -makepod probes::base> + +to generate the POD document. =cut @@ -24,6 +23,39 @@ $VERSION = 1.0; use strict; +sub pod_hash { + return { + name => <<DOC, +probes::base - Base Class for implementing SmokePing Probes +DOC + overview => <<DOC, +For the time being, please use the probes::FPing for +inspiration when implementing your own probes. +DOC + authors => <<'DOC', +Tobias Oetiker <tobi@oetiker.ch> +DOC + }; +} + +sub pod { + my $class = shift; + my $pod = ""; + my $podhash = $class->pod_hash; + $podhash->{synopsys} = $class->pod_synopsys; + $podhash->{variables} = $class->pod_variables; + for my $what (qw(name overview synopsys description variables authors notes bugs see_also)) { + my $contents = $podhash->{$what}; + next if not defined $contents or $contents eq ""; + $pod .= "=head1 " . uc $what . "\n\n"; + $pod .= $contents; + chomp $pod; + $pod .= "\n\n"; + } + $pod .= "=cut"; + return $pod; +} + sub new($$) { my $this = shift; @@ -214,4 +246,155 @@ sub target_count { return scalar keys %{$self->{targets}}; } +sub probevars { + return { + step => { + _re => '\d+', + _example => 300, + _doc => <<DOC, +Duration of the base interval that this probe should use, if different +from the one specified in the 'Database' section. Note that the step in +the RRD files is fixed when they are originally generated, and if you +change the step parameter afterwards, you'll have to delete the old RRD +files or somehow convert them. (This variable is only applicable if +the variable 'concurrentprobes' is set in the 'General' section.) +DOC + }, + offset => { + _re => '(\d+%|random)', + _re_error => + "Use offset either in % of operation interval or 'random'", + _example => '50%', + _doc => <<DOC, +If you run many probes concurrently you may want to prevent them from +hitting your network all at the same time. Using the probe-specific +offset parameter you can change the point in time when each probe will +be run. Offset is specified in % of total interval, or alternatively as +'random', and the offset from the 'General' section is used if nothing +is specified here. Note that this does NOT influence the rrds itself, +it is just a matter of when data acqusition is initiated. +(This variable is only applicable if the variable 'concurrentprobes' is set +in the 'General' section.) +DOC + }, + pings => { + _re => '\d+', + _example => 20, + _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 + }, + _mandatory => [], + }; +} + +sub targetvars { + return {_mandatory => []}; +} + +# a helper method that combines two var hash references +# and joins their '_mandatory' lists. +sub _makevars { + my ($class, $from, $to) = @_; + for (keys %$from) { + if ($_ eq '_mandatory') { + push @{$to->{_mandatory}}, @{$from->{$_}}; + next; + } + $to->{$_} = $from->{$_}; + } + return $to; +} + +sub pod_synopsys { + my $class = shift; + my $classname = ref $class||$class; + $classname =~ s/^probes:://; + + my $probevars = $class->probevars; + my $targetvars = $class->targetvars; + my $pod = <<DOC; + *** Probes *** + + +$classname + +DOC + $pod .= $class->_pod_synopsys($probevars); + my $targetpod = $class->_pod_synopsys($targetvars); + $pod .= "\n # The following variables can be overridden in each target section\n$targetpod" + if defined $targetpod and $targetpod ne ""; + $pod .= <<DOC; + + # [...] + + *** Targets *** + + probe = $classname # if this should be the default probe + + # [...] + + + mytarget + # probe = $classname # if the default probe is something else + host = my.host +DOC + $pod .= $targetpod + if defined $targetpod and $targetpod ne ""; + + return $pod; +} + +# synopsys for one hash ref +sub _pod_synopsys { + my $class = shift; + my $vars = shift; + my %mandatory; + $mandatory{$_} = 1 for (@{$vars->{_mandatory}}); + my $pod = ""; + for (sort keys %$vars) { + next if /^_mandatory$/; + my $val = $vars->{$_}{_example}; + $val = $vars->{$_}{_default} + if exists $vars->{$_}{_default} + and not defined $val; + $pod .= " $_ = $val"; + $pod .= " # mandatory" if $mandatory{$_}; + $pod .= "\n"; + } + return $pod; +} + +sub pod_variables { + my $class = shift; + my $probevars = $class->probevars; + my $pod = "Supported probe-specific variables:\n\n"; + $pod .= $class->_pod_variables($probevars); + return $pod; +} + +sub _pod_variables { + my $class = shift; + my $vars = shift; + my $pod = "=over\n\n"; + my %mandatory; + $mandatory{$_} = 1 for (@{$vars->{_mandatory}}); + for (sort keys %$vars) { + next if /^_mandatory$/; + $pod .= "=item $_\n\n"; + $pod .= $vars->{$_}{_doc}; + chomp $pod; + $pod .= "\n\n"; + $pod .= "Example value: " . $vars->{$_}{_example} . "\n\n" + if exists $vars->{$_}{_example}; + $pod .= "Default value: " . $vars->{$_}{_default} . "\n\n" + if exists $vars->{$_}{_default}; + $pod .= "This setting is mandatory.\n\n" + if $mandatory{$_}; + } + $pod .= "=back\n\n"; + return $pod; +} 1; diff --git a/lib/probes/basefork.pm b/lib/probes/basefork.pm index 9fd3f14..0eb213b 100644 --- a/lib/probes/basefork.pm +++ b/lib/probes/basefork.pm @@ -1,50 +1,41 @@ package probes::basefork; -my $DEFAULTFORKS = 5; - -=head1 NAME - -probes::basefork - Yet Another Base Class for implementing SmokePing Probes - -=head1 OVERVIEW - -Like 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. +=head1 301 Moved Permanently -=head1 SYNOPSYS +This is a Smokeping probe module. Please use the command - *** Probes *** +C<smokeping -man probes::basefork> - + MyForkingProbe - # run this many concurrent processes - forks = 10 - # how long does a single 'ping' take - timeout = 10 - # how many pings to send - pings = 10 +to view the documentation or the command - + MyOtherForkingProbe - # we don't want any concurrent processes at all for some reason. - forks = 1 +C<smokeping -makepod probes::basefork> - *** Targets *** +to generate the POD document. - menu = First - title = First - host = firsthost - probe = MyForkingProbe +=cut - menu = Second - title = Second - host = secondhost - probe = MyForkingProbe - +PROBE_CONF - pings = 20 +use strict; +use base qw(probes::basevars); +use Symbol; +use Carp; +use IO::Select; +use POSIX; # for ceil() and floor() +use Config; # for signal names -=head1 DESCRIPTION +my $DEFAULTFORKS = 5; +sub pod_hash { + return { + name => <<DOC, +probes::basefork - Yet Another Base Class for implementing SmokePing Probes +DOC + overview => <<DOC, +Like 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 @@ -60,7 +51,7 @@ the target that is to be measured. The contents of the hash are described in I<probes::basevars>(3pm). The number of concurrent processes is determined by the probe-specific -variable `forks' and is 5 by default. If there are more +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. @@ -69,37 +60,24 @@ 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 override the -default by providing a TimeOut method which returns an integer. +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 probe-specific variable -'pings', and it can be overridden by each target in the 'PROBE_CONF' -section. - -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 BUGS - -The timeout code has only been tested on Linux. - -=head1 SEE ALSO - +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, probes::basevars(3pm), probes::EchoPing(3pm) - -=cut - -use strict; -use base qw(probes::basevars); -use Symbol; -use Carp; -use IO::Select; -use POSIX; # for ceil() and floor() -use Config; # for signal names +DOC + } +} my %signo; my @signame; @@ -122,9 +100,41 @@ sub pingone { croak "pingone: this must be overridden by the subclass"; } -sub TimeOut { - # probes which require more time may want to provide their own implementation. - return 5; +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 { @@ -133,20 +143,25 @@ sub ping { my @targets = @{$self->targets}; return unless @targets; - my $forks = $self->{properties}{forks} || $DEFAULTFORKS; - - my $timeout = $self->{properties}{timeout}; - unless (defined $timeout and $timeout > 0) { - my $maxpings = 0; - for (@targets) { - my $p = $self->pings($_); - $maxpings = $p if $p > $maxpings; - } - $timeout = $maxpings * $self->TimeOut(); + 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 per target $timeout"); + $self->do_debug("forks $forks, timeout for each target $timeout"); while (@targets) { my %targetlookup; @@ -239,4 +254,13 @@ 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; diff --git a/lib/probes/basevars.pm b/lib/probes/basevars.pm index 19f21e0..8862395 100644 --- a/lib/probes/basevars.pm +++ b/lib/probes/basevars.pm @@ -1,84 +1,68 @@ package probes::basevars; -=head1 NAME +=head1 301 Moved Permanently -probes::basevars - Another Base Class for implementing SmokePing Probes - -=head1 OVERVIEW - -Like probes::base, but supports host-specific variables for the probe. +This is a Smokeping probe module. Please use the command -=head1 SYNOPSIS +C<smokeping -man probes::basevars> - *** Targets *** +to view the documentation or the command - menu = Top - title = Top Page +C<smokeping -makepod probes::basevars> - + branch_1 - menu = First menu - title = First title - host = host1 - ++ PROBE_CONF - # vars for host host1 - var1 = foo - var2 = bar - - ++ branch_1_2 - menu = Second menu - title = Second title - host = host2 - +++ PROBE_CONF - # vars for host host2 - # var1 and var2 are propagated from above, override var2 - var2 = fii +to generate the POD document. - + branch_2 - # var1 and var2 are undefined here +=cut -=head1 DESCRIPTION +use strict; +use probes::base; +use base qw(probes::base); +my $e = "="; +sub pod_hash { + return { + name => <<DOC, +probes::basevars - Another Base Class for implementing SmokePing Probes +DOC + overview => <<DOC, +Like probes::base, but supports host-specific variables for the probe. +DOC + description => <<DOC, Provides the method `targets' that returns a list of hashes. The hashes contain the entries: -=over +${e}over -=item addr +${e}item addr The address of the target. -=item vars +${e}item vars A hash containing variables defined in the corresponding -`PROBE_CONF' config section. +config section. -=item tree +${e}item tree The unique index that `probe::base' uses for targets. There's also the method 'vars' that returns the abovementioned hash corresponding to the 'tree' index parameter. -=back - -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 BUGS - +${e}back +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC + bugs => <<DOC, Uses `probes::base' internals too much to be a derived class, but I didn't want to touch the base class directly. - -=head1 SEE ALSO - +DOC + see_also => <<DOC, probes::base(3pm), probes::EchoPing(3pm) - -=cut - -use strict; -use probes::base; -use base qw(probes::base); +DOC + } +} sub add($$) { @@ -86,7 +70,7 @@ sub add($$) my $tree = shift; $self->{targets}{$tree} = shift; - $self->{PROBE_CONF}{$tree} = $tree->{PROBE_CONF}; + $self->{vars}{$tree} = { %{$self->{properties}}, %$tree }; } sub targets { @@ -100,8 +84,7 @@ sub targets { for (@$addr) { @{$copy{$_}} = @{$self->{addrlookup}{$_}} unless exists $copy{$_}; my $tree = pop @{$copy{$_}}; - push @targets, { addr => $_, vars => $self->{PROBE_CONF}{$tree}, - tree => $tree }; + push @targets, { addr => $_, vars => $self->{vars}{$tree}, tree => $tree }; } return \@targets; } @@ -109,7 +92,7 @@ sub targets { sub vars { my $self = shift; my $tree = shift; - return $self->{PROBE_CONF}{$tree}; + return $self->{vars}{$tree}; } sub ProbeDesc { diff --git a/lib/probes/passwordchecker.pm b/lib/probes/passwordchecker.pm index 8fad4f9..87d72eb 100644 --- a/lib/probes/passwordchecker.pm +++ b/lib/probes/passwordchecker.pm @@ -1,15 +1,37 @@ package probes::passwordchecker; -=head1 NAME +=head1 301 Moved Permanently -probes::passwordchecker - A Base Class for implementing SmokePing Probes +This is a Smokeping probe module. Please use the command + +C<smokeping -man probes::passwordchecker> + +to view the documentation or the command + +C<smokeping -makepod probes::passwordchecker> + +to generate the POD document. + +=cut -=head1 OVERVIEW +use strict; +use probes::basefork; +use base qw(probes::basefork); +use Carp; +my $e = "="; +sub pod_hash { + return { + name => <<DOC, +probes::passwordchecker - A Base Class for implementing SmokePing Probes +DOC + overview => <<DOC, Like probes::basefork, but supports a probe-specific configuration file for storing passwords and a method for accessing them. +DOC -=head1 SYNOPSYS + description => <<DOC, +${e}head2 synopsys with more detail SmokePing main configuration file: @@ -26,7 +48,7 @@ The specified password file: host2:sue:notasecreteither -=head1 DESCRIPTION +${e}head2 Actual description In implementing authentication probes, it might not be desirable to store the necessary cleartext passwords in the SmokePing main configuration @@ -41,37 +63,47 @@ in the probe-specific variable `passwordfile'. The passwords can later be accessed and modified by the B<password> method, that needs the corresponding host and username as arguments. -=head1 PASSWORD FILE FORMAT +${e}head2 Password file format The password file format is simply one line for each triplet of host, username and password, separated from each other by colons (:). Comment lines, starting with the `#' sign, are ignored, as well as empty lines. +DOC + authors => <<'DOC', +Niko Tyni <ntyni@iki.fi> +DOC -=head1 AUTHOR - -Niko Tyni E<lt>ntyni@iki.fiE<gt> - -=head1 BUGS - + bugs => <<DOC, The need for storing cleartext passwords can be considered a bug in itself. +DOC -=head1 SEE ALSO - + see_also => <<DOC, probes::basefork(3pm), probes::Radius(3pm), probes::LDAP(3pm) - -=cut - -use strict; -use probes::basefork; -use base qw(probes::basefork); -use Carp; +DOC + } +} sub ProbeDesc { return "probe that can fork, knows about passwords and doesn't override the ProbeDesc method"; } +sub probevars { + my $class = shift; + return $class->_makevars($class->SUPER::probevars, { + passwordfile => { + _doc => "Location of the file containing usernames and passwords.", + _example => '/some/place/secret', + _sub => sub { + my $val = shift; + -r $val or return "ERROR: password file $val is not readable."; + return undef; + }, + }, + }); +} + sub new { my $proto = shift; my $class = ref($proto) || $proto; diff --git a/lib/probes/skel.pm b/lib/probes/skel.pm new file mode 100644 index 0000000..5438e1f --- /dev/null +++ b/lib/probes/skel.pm @@ -0,0 +1,134 @@ +package probes::skel; + +=head1 301 Moved Permanently + +This is a Smokeping probe module. Please use the command + +C<smokeping -man probes::skel> + +to view the documentation or the command + +C<smokeping -makepod probes::skel> + +to generate the POD document. + +=cut + +use strict; +use base qw(probes::basefork); +# or, alternatively +# use base qw(probes::base); +use Carp; + +sub pod_hash { + return { + name => <<DOC, +probes::skel - a skeleton for Smokeping Probes +DOC + description => <<DOC, +This is a non-functional module that is intended to act as a +basis for creation of new probes. See the L<smokeping_extend> +document for more information. +DOC + authors => <<'DOC', + Niko Tyni <ntyni@iki.fi>, +DOC + see_also => <<DOC +The L<smokeping_extend> document +DOC + }; +} + +sub new($$$) +{ + my $proto = shift; + my $class = ref($proto) || $proto; + my $self = $class->SUPER::new(@_); + + # no need for this if we run as a cgi + unless ( $ENV{SERVER_SOFTWARE} ) { + # if you have to test the program output + # or something like that, do it here + # and bail out if necessary + }; + + return $self; +} + +# This is where you should declare your probe-specific variables. +# The example shows the common case of checking the availability of +# the specified binary. + +sub probevars { + my $class = shift; + return $class->_makevars($class->SUPER::probevars, { + #_mandatory => [ 'binary' ], + #binary => { + # _doc => "The location of your pingpong binary.", + # _example => '/usr/bin/pingpong', + # _sub => sub { + # my $val = shift; + # return "ERROR: pingpong 'binary' does not point to an executable" + # unless -f $val and -x _; + # return undef; + # }, + #}, + }); +} + +# Here's the place for target-specific variables + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + #weight => { _doc => "The weight of the pingpong ball in grams", + # _example => 15 + #}, + }); +} + +sub ProbeDesc($){ + my $self = shift; + return "pingpong points"; +} + +# this is where the actual stuff happens +# you can access the probe-specific variables +# via the $self->{properties} hash and the +# target-specific variables via $target->{vars} + +# If you based your class on 'probes::base', +# you'd have to provide a "ping" method instead +# of "pingone" + +sub pingone ($){ + my $self = shift; + my $target = shift; + + # my $binary = $self->{properties}{binary}; + # my $weight = $target->{vars}{weight} + # my $count = $self->pings($target); # the number of pings for this targets + + # ping one target + + # execute a command and parse its output + # you should return a sorted array of the measured latency times + # it could go something like this: + + my @times; + + #for (1..$count) { + # open(P, "$cmd 2>&1 |") or croak("fork: $!"); + # while (<P>) { + # /time: (\d+\.\d+)/ and push @times, $1; + # } + # close P; + #} + + + return @times; +} + +# That's all, folks! + +1; diff --git a/lib/probes/telnetIOSPing.pm b/lib/probes/telnetIOSPing.pm index e591563..e0af7ac 100644 --- a/lib/probes/telnetIOSPing.pm +++ b/lib/probes/telnetIOSPing.pm @@ -1,67 +1,39 @@ package probes::telnetIOSPing; -=head1 NAME +=head1 301 Moved Permanently -probes::telnetIOSPing - Cisco IOS Probe for SmokePing - -=head1 SYNOPSIS - - *** Probes *** - + telnetIOSPing - packetsize = 56 - forks = 1 - - ++ PROBE_CONF - iospass = password - iosuser = user - target = 192.168.1.1 - source = 192.168.2.1 - psource = 192.168.2.129 - -=head1 DESCRIPTION +This is a Smokeping probe module. Please use the command -Integrates Cisco IOS as a probe into smokeping. Uses the telnet protocol -to run a ping from an IOS device (source) to another device (target). -This probe basically uses the "extended ping" of the Cisco IOS. You have -the option to specify which interface the ping is sourced from as well. +C<smokeping -man probes::telnetIOSPing> -=head1 OPTIONS +to view the documentation or the command -The iosuser, iospass, source, and target options are mandatory. - -The (optional) packetsize option lets you configure the packetsize for -the pings sent. The default size is 56. +C<smokeping -makepod probes::telnetIOSPing> -The (optional) forks options lets you configure the number of -simultaneous remote pings to be run. NB Some IOS devices have a -maximum of 5 VTYs available, so be careful not to hit a limit. +to generate the POD document. -The source option specifies the IOS device to which we telnet. This -is an IP address of an IOS Device that you/your server: - 1) Have the ability to telnet to - 2) Have a valid username and password for - -The target option specifies the device you wish to ping from your IOS -Device. - -The (optional) psource option specifies an alternate IP address or -Interface from which you wish to source your pings from. Routers -can have many many IP addresses, and interfaces. When you ping from a -router you have the ability to choose which interface and/or which IP -address the ping is sourced from. Specifying an IP/interface does not -necessarily specify the interface from which the ping will leave, but -will specify which address the packet(s) appear to come from. If this -option is left out the IOS Device will source the packet automatically -based on routing and/or metrics. If this doesn't make sense to you -then just leave it out. +=cut -The iosuser option allows you to specify a username that has ping -capability on the IOS Device. +use strict; -The iospass option allows you to specify the password for the username -specified with the option iosuser. +use base qw(probes::basefork); +use Net::Telnet (); +use Carp; -=head1 IOS CONFIGURATION +my $e = "="; +sub pod_hash { + return { + name => <<DOC, +probes::telnetIOSPing - Cisco IOS Probe for SmokePing +DOC + description => <<DOC, +Integrates Cisco IOS as a probe into smokeping. Uses the telnet protocol +to run a ping from an IOS device (source) to another device (host). +This probe basically uses the "extended ping" of the Cisco IOS. You have +the option to specify which interface the ping is sourced from as well. +DOC + notes => <<DOC, +${e}head2 IOS configuration The IOS device should have a username/password configured, as well as the ability to connect to the VTY(s). @@ -75,14 +47,15 @@ eg: transport input telnet ! -=head1 NOTES +Some IOS devices have a maximum of 5 VTYs available, so be careful not +to hit a limit with the 'forks' variable. -=head2 Requirements +${e}head2 Requirements This module requires the Net::Telnet module for perl. This is usually included on most newer OSs which include perl. -=head2 Debugging +${e}head2 Debugging There is some VERY rudimentary debugging code built into this module (it's based on the debugging code written into Net::Telnet). It will log @@ -91,7 +64,7 @@ These files will be written out into your current working directory (CWD). You can change the names of these files to something with more meaning to you. -=head2 Password authentication +${e}head2 Password authentication You should be advised that the authentication method of telnet uses clear text transmissions...meaning that without proper network security @@ -104,7 +77,7 @@ Having said this, don't be too scared of telnet. Remember, the original IOSPing module used RSH, which is even more scary to use from a security perspective. -=head2 Ping packet size +${e}head2 Ping packet size The FPing manpage has the following to say on the topic of ping packet size: @@ -116,9 +89,8 @@ header (normally 20 bytes) and ICMP header (8 bytes), so the minimum total size is 40 bytes. Default is 56, as in ping. Maximum is the theoretical maximum IP datagram size (64K), though most systems limit this to a smaller, system-dependent number. - -=head1 AUTHOR - +DOC + authors => <<'DOC', John A Jackson <geonjay@infoave.net> based HEAVILY on probes::IOSPing by @@ -128,14 +100,9 @@ Paul J Murphy <paul@murph.org> based on probes::FPing by Tobias Oetiker <tobi@oetiker.ch> - -=cut - -use strict; - -use base qw(probes::basefork); -use Net::Telnet (); -use Carp; +DOC + } +} sub new($$$) { @@ -145,10 +112,6 @@ sub new($$$) # no need for this if we run as a cgi unless ( $ENV{SERVER_SOFTWARE} ) { - croak "ERROR: IOSPing packetsize must be between 12 and 64000" - if $self->{properties}{packetsize} and - ( $self->{properties}{packetsize} < 12 or $self->{properties}{packetsize} > 64000 ); - $self->{pingfactor} = 1000; # Gives us a good-guess default print "### assuming you are using an IOS reporting in miliseconds\n"; }; @@ -158,7 +121,7 @@ sub new($$$) sub ProbeDesc($){ my $self = shift; - my $bytes = $self->{properties}{packetsize} || 56; + my $bytes = $self->{properties}{packetsize}; return "InfoAve Cisco IOS - ICMP Echo Pings ($bytes Bytes)"; } @@ -166,13 +129,13 @@ sub pingone ($$){ my $self = shift; my $target = shift; my $source = $target->{vars}{source}; - my $dest = $target->{vars}{target}; + my $dest = $target->{vars}{host}; my $psource = $target->{vars}{psource} || ""; my $port = 23; my @output = (); my $login = $target->{vars}{iosuser}; my $pssword = $target->{vars}{iospass}; - my $bytes = $self->{properties}{packetsize} || 56; + my $bytes = $self->{properties}{packetsize}; my $pings = $self->pings($target); # do NOT call superclass ... the ping method MUST be overwriten @@ -252,4 +215,69 @@ sub pingone ($$){ return @times; } +sub probevars { + my $class = shift; + return $class->_makevars($class->SUPER::probevars, { + packetsize => { + _doc => <<DOC, +The (optional) packetsize option lets you configure the packetsize for +the pings sent. +DOC + _default => 56, + _re => '\d+', + _sub => sub { + my $val = shift; + return "ERROR: packetsize must be between 12 and 64000" + unless $val >= 12 and $val <= 64000; + return undef; + }, + }, + }); +} + +sub targetvars { + my $class = shift; + return $class->_makevars($class->SUPER::targetvars, { + _mandatory => [ 'iosuser', 'iospass', 'source' ], + source => { + _doc => <<DOC, +The source option specifies the IOS device to which we telnet. This +is an IP address of an IOS Device that you/your server: + 1) Have the ability to telnet to + 2) Have a valid username and password for +DOC + _example => "192.168.2.1", + }, + psource => { + _doc => <<DOC, +The (optional) psource option specifies an alternate IP address or +Interface from which you wish to source your pings from. Routers +can have many many IP addresses, and interfaces. When you ping from a +router you have the ability to choose which interface and/or which IP +address the ping is sourced from. Specifying an IP/interface does not +necessarily specify the interface from which the ping will leave, but +will specify which address the packet(s) appear to come from. If this +option is left out the IOS Device will source the packet automatically +based on routing and/or metrics. If this doesn't make sense to you +then just leave it out. +DOC + _example => "192.168.2.129", + }, + iosuser => { + _doc => <<DOC, +The iosuser option allows you to specify a username that has ping +capability on the IOS Device. +DOC + _example => 'user', + }, + iospass => { + _doc => <<DOC, +The iospass option allows you to specify the password for the username +specified with the option iosuser. +DOC + _example => 'password', + }, + }); +} + 1; |