#!/usr/bin/perl -w # # Plugin to monitor dhcp3 leases # # Usage: copy or link into /etc/munin/plugins # # Parameters: # # config (required) # autoconf (optional - used by munin-config) # # Config variables: # # user - run by user that can read lease files [root] # leasefile - What file to read leases from # configfile - What file to read configuration from # # Requires: # Net::Netmask # HTTP::Date # # $Log$ # Revision 1.1 2004/12/10 13:16:19 jimmyo # Added plugin generic/dhcpd3, created by Rune N. Skillingstad. # # # # Magic markers (optinal - used by munin-config and some installation # scripts): # #%# family=contrib #%# capabilities=autoconf my $ret = undef; if(! eval "require Net::Netmask") { $ret = "Net::Netmask not found"; } if(! eval "require HTTP::Date") { $ret = "HTTP::Date not found"; } use strict; my %leases = (); my %networks = (); my %ips = (); my $DEBUG = 0; my $LEASEFILE = $ENV{leasefile} || "/var/lib/dhcp/dhcpd.leases"; my $CONFIGFILE = $ENV{configfile} || "/etc/dhcpd.conf"; if($ARGV[0] and $ARGV[0] eq "autoconf" ) { if($ret) { print "no ($ret)\n"; exit 1; } if(-f $LEASEFILE) { if(-r $LEASEFILE) { if(-f $CONFIGFILE) { if(-r $CONFIGFILE) { print "yes\n"; exit 0; } else { print "no (config file not readable)\n"; } } else { print "no (config gile not found)\n"; } } else { print "no (leasefile not readable)\n"; } } else { print "no (leasefile not found)\n"; } exit 1; } if (! -f $LEASEFILE and ! -f $CONFIGFILE) { print "net.value U\n"; exit 0; } if($DEBUG) { print "CONFIGFILE == $CONFIGFILE\nLEASEFILE == $LEASEFILE\n"; } Net::Netmask->import(); HTTP::Date->import(); parseconfig(); if($ARGV[0] and $ARGV[0] eq "config") { print "graph_title dhcp leases\n"; print "graph_args --base 1000 -v leases -l 0\n"; print "graph_order ".join(" ",sort(keys(%leases)))."\n"; foreach my $network (sort(keys %leases)) { my $name = $network; $name =~ s/_/\./g; $name =~ s/\.\./\//g; print "$network.label $name\n"; } exit 0; } parseleases(); foreach my $network (sort(keys %leases)) { print "$network.value ".$leases{$network}."\n"; } sub parseconfig { open(IN, "<$CONFIGFILE") or exit 4; while() { if(/subnet\s+(\d+\.\d+\.\d+\.\d+)\s+netmask\s+(\d+\.\d+\.\d+\.\d+)/ && ! /^\s*#/) { initnet($1,$2); } } close(IN); } sub parseleases { my $ip = 0; my $abandon = 0; my $time = time(); open(IN, "<$LEASEFILE") or exit 4; while() { if(/lease\s+(\d+\.\d+\.\d+\.\d+)\s+\{/) { print "in $1\n" if $DEBUG; $ip = $1; } if($ip && /ends\s+\d+\s+([^;]+);/) { # 2037/12/31 23:59:59 is max date on perl <= 5.6 print "end $1\n" if $DEBUG; my $end = HTTP::Date::str2time($1, "GMT"); # we asume that missing $end is valid due to # restrictions in Time::Local on perl <= 5.6 if($end && $end < $time) { print "old $end $time:(\n" if $DEBUG; $abandon = 1; } } if($ip && /^\s*abandoned;$/) { print "abandoned\n" if $DEBUG; $abandon = 1; } if($ip && /^\s*\}\s*$/) { my $net = checkip($ip); if($net && !$abandon) { if(!counted($ip)) { $leases{$net}++; } } $abandon = 0; $ip = 0; print "out\n\n" if $DEBUG; } } close(IN); } sub initnet { my ($net, $mask) = @_; my $block = new Net::Netmask($net, $mask); $networks{$block->desc()} = $block; my $name = $block->desc(); $name =~ s/\//__/g; $name =~ s/\./_/g; $leases{$name} = 0; } sub checkip { my ($ip) = @_; foreach my $block (keys %networks) { if($networks{$block}->match($ip)) { my $name = $block; $name =~ s/\//__/g; $name =~ s/\./_/g; return $name; } } return 0; } sub counted { my ($ip) = @_; if($ips{$ip}) { print "$ip already counted\n" if $DEBUG; return 1; } $ips{$ip} = $ip; print "Counted $ip once!\n" if $DEBUG; return 0; } exit();