#!/usr/bin/perl -w =head1 NAME squid - Multigraph-capable plugin to monitor Squid =head1 NOTES This plugin will produce multiple graphs showing: - the number of requests (replaces squid_requests); - the traffic (replaces squid_traffic); - the size of the cache (replaces squid_cache); - the traffic to the ICP peers (replaces squid_icp); - the mean size of stored objects (replaces squid_objectsize). =head1 CONFIGURATION The following configuration parameters are used by this plugin [squid] env.host - hostname to connect to env.port - port number to connect to env.username - username used for authentication env.password - password used for authentication =head2 DEFAULT CONFIGURATION [squid] env.host 127.0.0.1 env.port 3128 =head2 WILDCARD CONFIGURATION It's possible to use the plugin in a virtual-node capacity, in which case the host configuration will default to the hostname following the underscore: [squid_someserver] env.host someserver env.port 3128 =head1 AUTHOR Copyright (C) 2013 Diego Elio Pettenò Copyright (C) 2008 Bjorn Ruberg Copyright (C) 2004 Audun Ytterdal Copyright (C) 2004 Jimmy Olsen Copyright (C) 2004 Tore Anderson =head1 LICENSE Gnu GPLv2 =begin comment 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. =end comment =head1 MAGIC MARKERS #%# family=auto #%# capabilities=autoconf =cut use strict; use Munin::Plugin; use Munin::Plugin::Framework; use IO::Socket::INET; my $plugin = Munin::Plugin::Framework->new; $plugin->{hostname} = $1 if $Munin::Plugin::me =~ /_([^_]+)$/; my $peeraddr = $ENV{'host'} || $plugin->{hostname} || '127.0.0.1'; my $peerport = $ENV{'port'} || '3128'; my $username = $ENV{'username'}; my $password = $ENV{'password'}; my $requirements = undef; if (! eval "require HTTP::Headers" or ! eval "require HTTP::Response" ) { $requirements = "HTTP::Message not found"; } sub getobject { my $socket = IO::Socket::INET->new(PeerAddr => $peeraddr, PeerPort => $peerport, Proto => 'tcp', Timeout => 25); if ( ! $socket ) { $requirements = $!; return undef; } my ($object) = @_; my $request_line = sprintf("GET cache_object://%s/%s HTTP/1.0\r\n", $peeraddr, $object); my $headers = HTTP::Headers->new('Accept' => '*/*', 'User-Agent' => sprintf("munin/%s (squid)", $Munin::Common::Defaults::MUNIN_VERSION) ); if ( $username and $password ) { $headers->authorization_basic($username, $password); $headers->proxy_authorization_basic($username, $password); } $socket->print($request_line . $headers->as_string("\r\n") . "\r\n"); my $response = HTTP::Response->parse(join('', $socket->getlines)); $socket->close(); return $response; } sub get_icp { my $server_list = getobject('server_list'); if ( not defined $server_list ) { return undef; } if ( $server_list->{content} =~ /There are no neighbors/ ) { return undef; } my @lines = split(/\r\n/, $server_list->{content}); my $ret; my $id = ""; for (my $i = 0; $i <= $#lines; $i++) { chomp $lines[$i]; if ($lines[$i] =~ /Address[^:]+:\s*([\d\.]+)\s*$/) { my $host = $1; $id = "h" . $host; $id =~ s/\.//g; my $h; if ($h = Net::hostent::gethost ($host)) { $ret->{$id}->{host} = lc $h->name; } else { $ret->{$id}->{host} = $host; } } elsif ($lines[$i] =~ /FETCHES\s*:\s*(\d+)/) { $ret->{$id}->{fetches} = $1; } } } my $counters = getobject('counters'); my $storedir = getobject('storedir'); my $info = getobject('info'); my $icp_data = get_icp; if ( $requirements ) { $plugin->{autoconf} = "no ($requirements)"; } elsif ( !$counters->is_success ) { $plugin->{autoconf} = "no ($counters->status_line)"; $plugin->{autoconf} =~ s/[\r\n]//g; } if ( defined $counters && $counters->{is_success} ) { my $hits = ($counters->content =~ /client_http\.hits = ([0-9]+)/) ? $1 : "U"; my $errors = ($counters->content =~ /client_http\.errors = ([0-9]+)/) ? $1 : "U"; my $requests = ($counters->content =~ /client_http\.requests = ([0-9]+)/) ? $1 : undef; my $misses = $requests ? ($requests - $errors - $hits) : "U"; my $in = ($counters->content =~ /client_http\.kbytes_in = ([0-9]+)/) ? ($1 * 1024) : "U"; my $out = ($counters->content =~ /client_http\.kbytes_out = ([0-9]+)/) ? ($1 * 1024) : "U"; my $hits_out = ($counters->content =~ /client_http\.hit_kbytes_out = ([0-9]+)/) ? ($1 * 1024) : "U"; $plugin->add_graphs ( squid_requests => { title => "Squid client requests", args => "--base 1000 -l 0", vlabel => "requests per \${graph_period}", order => "hits errors misses", total => "total", category => "network", fields => { hits => { type => "DERIVE", draw => "AREASTACK", min => 0, value => $hits, }, errors => { type => "DERIVE", draw => "AREASTACK", min => 0, value => $errors, }, misses => { type => "DERIVE", draw => "AREASTACK", min => 0, value => $misses, }, }, }, "squid_traffic" => { title => "Squid Traffic", args => "--base 1024 -l 0", vlabel => "bytes per \${graph_period}", order => "in out hits_out", fields => { in => { label => "received", type => "DERIVE", min => "0", value => $in, }, out => { label => "sent", type => "DERIVE", min => "0", value => $out, }, hits_out => { label => "sent from cache", type => "DERIVE", min => "0", value => $hits_out, }, }, }, ); } if ( defined $storedir && $storedir->is_success ) { my $max = ($storedir->content =~ /Maximum (?:Swap )?Size\s*:\s*([0-9]+) KB/) ? ($1 * 1024) : "U"; my $current = ($storedir->content =~ /Current (?:Store Swap )?Size\s*:\s*([0-9]+) KB/) ? ($1 * 1024) : "U"; $plugin->add_graphs ( "squid_swap" => { title => "Squid swap size", order => "max current", vlabel => "bytes", args => "--base 1024 -l 0", fields => { max => { label => "Maximum Swap Size", value => $max }, current => { label => "Current Store Swap Size", value => $current } } }, ); } if ( defined $info && $info->is_success ) { my $size = 'U'; if ( $info->content =~ /Mean Object Size:\s*([0-9.]+) ([KMG])B/i ) { $size = $1; my $unit = $2; if ( $unit eq 'K' ) { $size *= 1024; } elsif ( $unit eq 'M' ) { $size *= (1024*1024); } elsif ( $unit eq 'G' ) { $size *= (1024*1024*1024); } } $plugin->add_graphs ( "squid_objectsize" => { title => "Squid object size", vlabel => "bytes", args => "--base 1024 -l 0", fields => { size => { label => "Mean Object Size", value => $size } }, } ); } if ( $icp_data ) { $plugin->add_graphs ( "squid_icp" => { title => "Squid Relay Statistics", vlabel => "requests per \${graph_period}", args => "--base 1000 -l 0", total => "total", fields => {}, }, ); foreach my $i (sort keys %{$icp_data}) { $plugin->{graphs}->{"squid_icp"}->{fields}->{$i} = { label => $icp_data->{$i}->{host}, type => "DERIVE", min => 0, draw => "AREASTACK", value => $icp_data->{$i}->{fetches} }; } } $plugin->run;