From dca361d029727d22052708ec05bc166644b832b9 Mon Sep 17 00:00:00 2001 From: Artem Sheremet Date: Wed, 25 Jul 2012 04:02:08 +0300 Subject: [PATCH] Rewriting Ping plugin in perl * Merging existing plugins --- plugins/network/ping/ping | 259 ++++++++++++++++++++++++++------------ 1 file changed, 176 insertions(+), 83 deletions(-) diff --git a/plugins/network/ping/ping b/plugins/network/ping/ping index b56d6ac9..0ee6745d 100755 --- a/plugins/network/ping/ping +++ b/plugins/network/ping/ping @@ -1,98 +1,191 @@ -#!/bin/bash +#!/usr/bin/perl -w -# ping revision 2 (Jul 2012) -# -# This plugin shows ping statistics for several hosts on a single graph -# -# Configuration: -# Add [ping] section with 'hosts' set to space-separated list of hostnames or addresses. -# You can prefix host name with ':' to change IP resolution mode, -# e.g. 'AAAA:domain.tld' will request IPv6 record. -# Always prefix IPv6 addresses with 'AAAA:' (internal limitation). -# -# Dependencies: -# host util is required -# -# OS: -# Linux -# -# Author: Artem Sheremet -# +=cut -function normalize() { - echo ${1//[^a-zA-Z0-9]/_} +=head1 NAME + +ping - Plugin to monitor ping times + +=head1 CONFIGURATION + + ping_args - Arguments to ping (default "-c 2 -w 1") + ping_args2 - Arguments after the host name (required for Solaris) + ping - Ping program to use + ping6 - Ping program to use with IPv6 + hosts - List of comma-separated hosts to ping (IPv4/6 address or FQDN) + You can prepend host name with: + 'A:' - to resolve all IPv4 addresses + and create an entry per address (requires 'host' program) + 'AAAA:' - the same for IPv6 (requires 'host' program) + '6:' - do not try to resolve, but use ping6 for this host + names - Friendly display name of each host given in the "hosts" parameter (comma-separated list). + If not set, "hosts" elements will be used + fork - If set to non-zero, fork each ping into parallel process to ping asynchronously + +Configuration example + + [ping] + env.hosts mail.example.org,6:www.example.org,AAAA:search.example.org + env.names Mail,Web,Search + env.fork yes + +Configuration example for Solaris + + [multiping] + env.host www.example.org mail.example.org + env.ping_args -s + env.ping_args2 56 2 + +You can also append plugin name with '_packetloss' to get packet loss statistics. + +=head1 AUTHOR + +Original Perl script Copyright (C) 2008 Thomas Gutzler (thomas.gutzler@gmail.com) +Original Shell script Copyright (C) 2004 Jimmy Olsen + +=head1 LICENSE + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 dated June, +1991. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 MAGIC MARKERS + +#%# family=manual + +=cut + +use strict; +use Munin::Plugin; + +my $ping = exists $ENV{ping} ? $ENV{ping} : "ping"; +my $ping6 = exists $ENV{ping6} ? $ENV{ping6} : "ping6"; +my $ping_args = exists $ENV{ping_args} ? $ENV{ping_args} : "-c 2 -w 1"; +my $ping_args2 = exists $ENV{ping_args2} ? $ENV{ping_args2} : ""; +my $fork = exists $ENV{fork} ? $ENV{fork} : 0; +my $packetloss_mode = ($0 =~ /_packetloss$/); + +my @host_options = exists $ENV{hosts} ? split(/,/, $ENV{hosts}) : "A:www.google.com"; + +# bare addresses +my @host_addrs = @host_options; + +# ping program to use +my @host_ping = ($ping) x @host_options; + +# resolve record type, if specified +my @host_resolv = (0) x @host_options; + +my $config_mode = ($ARGV[0] and $ARGV[0] eq 'config'); +if ($config_mode) { + print "graph_title " . ($packetloss_mode ? "Ping packet loss" : "Ping times") . "\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel " . ($packetloss_mode ? "packets" : "seconds") . "\n"; + print "graph_category network\n"; + print "graph_info This graph shows ping " . ($packetloss_mode ? "packet loss" : "RTT") . " statistics.\n"; } -function host_type() { - case $1 in - *:*) - echo ${1/:*/} - ;; - *) - echo A - ;; - esac +for (my $host_option = 0; $host_option < @host_options; ++$host_option) { + my $h_addr = $host_options[$host_option]; + my @config = split(/:/, $h_addr, 2); + if ($#config) { + my $h_ping = $ping; + my $h_resolv = 0; + if ($config[0] eq "6") { + # user wants this host to be pinged via IPv6 + $h_ping = $ping6; + $h_addr = $config[1]; + } elsif ($config[0] eq "A") { + # user wants this host to be resolved via IPv4 + $h_resolv = "A"; + $h_addr = $config[1]; + } elsif ($config[0] eq "AAAA") { + # user wants this host to be resolved as IPv6 + $h_resolv = "AAAA"; + $h_addr = $config[1]; + $h_ping = $ping6; + } elsif ($config[0] =~ /^[[:xdigit:]]+$/) { + # this is IPv6 addr + $h_ping = $ping6; + } else { + # let 'host' decide what resolve type do we need + $h_resolv = $config[0]; + $h_addr = $config[1]; + } + $host_addrs[$host_option] = $h_addr; + $host_resolv[$host_option] = $h_resolv; + $host_ping[$host_option] = $h_ping; + } } -function host_name() { - echo "${1/*:/}" +# display names +my @host_names = exists $ENV{names} ? split(/,/, $ENV{names}) : @host_addrs; + +if (@host_names != @host_addrs) { + print "Warning: host names specified does not match addresses\n"; } -function ipof() { - host -t $(host_type $1) $(host_name $1) | - sort | grep -Eo 'address .*' | cut -d' ' -f2 | head -1 -} +# forked children +my @children = (); -if [[ "$1" == "config" ]]; then - echo 'graph_title Ping statistics -graph_vlabel Time,ms -graph_category network -graph_info ping statistics for several hosts' - for host in $hosts; do - echo "$(normalize $host).label $(host_name $host) ($(ipof $host))" - done - exit 0 -fi +for (my $host_i = 0; $host_i < @host_addrs; ++$host_i) { + my @h_addrs = ($host_addrs[$host_i]); + my $h_ping = $host_ping[$host_i]; + my $h_resolv = $host_resolv[$host_i]; + my $h_name = $host_names[$host_i]; -SYNC_FILE=/tmp/munin-ping-sync -echo -n >"$SYNC_FILE" - -COUNT=0 -for host in $hosts; do - { - case $(host_type $host) in - AAAA) - PING=ping6 - ;; - *) - PING=ping - ;; - esac - TIME=$($PING -w 1 -c 1 `host_name $host` | awk ' - BEGIN { - found = 0 - t = 0 + if ($h_resolv) { + # we have to resolve the host to (probably multiple) addresses + my $h_base_addr = pop @h_addrs; + my @host = `host -t $h_resolv $h_base_addr`; + chomp @host; + for (my $h_host_i = 0; $h_host_i < @host; ++$h_host_i) { + my $h_host = $host[$h_host_i]; + my ($h_addr) = $h_host =~ /address (.*)$/; + if ($h_addr) { + push @h_addrs, $h_addr; } + } + } - $1 == "rtt" { found = 1; t = $4; } - - END { - if (found == 1) { - split(t, parts, "/"); - print parts[1] - } else { - print "U" + for (my $h_addr_i = 0; $h_addr_i < @h_addrs; ++$h_addr_i) { + my $h_addr = $h_addrs[$h_addr_i]; + my $h_cur_name = $h_resolv ? $h_name . " ($h_addr)" : $h_name; + my $h_norm_name = clean_fieldname($h_cur_name); + if ($config_mode) { + print "$h_norm_name.min 0\n$h_norm_name.label $h_cur_name\n$h_norm_name.draw LINE2\n"; + } else { + my $pid = $fork ? fork() : -1; + if ($pid <= 0) { + # either we can't fork, or don't want to, or we are child + my @ping = `$h_ping $ping_args $h_addr $ping_args2`; + chomp @ping; + my $ping = join(" ", @ping); + my $ping_time = ($1 / 1000) if ($ping =~ m@min/avg/max.*\s\d+(?:\.\d+)?/(\d+(?:\.\d+)?)/\d+(?:\.\d+)?@); + my $packet_loss = $1 if ($ping =~ /(\d+)% packet loss/); + print "$h_norm_name.value ". ($packetloss_mode ? $packet_loss : $ping_time) . "\n"; + if ($pid == 0) { + # this is a child process, don't forget to exit + exit(0); } + } else { + # in parent + push @children, $pid; } - ') + } + } +} - echo $(normalize $host).value $TIME - echo ok >>"$SYNC_FILE" - } & - let COUNT=$COUNT+1 -done - -# wait for background processes to finish -while [ $(wc -l <"$SYNC_FILE") -lt $COUNT ]; do - sleep 0.2 -done +for (my $child_i = 0; $child_i < @children; ++$child_i) { + waitpid($children[$child_i], 0); +}