Lippe:Monitoring RAM Datendurchsatz vergebene IPs

Aus wiki.freifunk.net
Zur Navigation springenZur Suche springen


Monitoring des freien Arbeitsspeichers unter Linux

Da der freie Speicher unter Linux aufgrund der Speicherverwaltung anders gemonitort werden muss als unter Windows habe ich das folgende Script verwendet:



!/bin/sh                                                                                                                                                                                                                        
#                                                                                                                                                                                                                                
# Plugin to check system memory                                                                                                                                                                                                  
# by hugme (nagios@hugme.org)                                                                                                                                                                                                    
# You can find my checks here: https://github.com/hugme/Nag_checks                                                                                                                                                               
# Nagios script to check memory usage on linux server                                                                                                                                                                            
# version 1.2.0                                                                                                                                                                                                                  
#                                                                                                                                                                                                                                
##########################################################                                                                                                                                                                       
                                                                                                                                                                                                                                 
MEMINFO="/proc/meminfo"                                                                                                                                                                                                          
                                                                                                                                                                                                                                 
##########################################################                                                                                                                                                                       
# We call them functions because they're fun                                                                                                                                                                                     
##########################################################                                                                                                                                                                       
                                                                                                                                                                                                                                 
print_help() {                                                                                                                                                                                                                   
cat << EOF                                                                                                                                                                                                                       
Linux Memory Plugin for Nagios                                                                                                                                                                                                   
Copyright (c) hugme (nagios@hugme.org)                                                                                                                                                                                           
Version: 1.2.0                                                                                                                                                                                                                   
Last Modified: 10-07-2014                                                                                                                                                                                                        
License: This software can be used for free unless I meet you, then you owe me lunch.                                                                                                                                            
                                                                                                                                                                                                                                 
Usage: check_linux_memory -w [warning %] -c [critical %]                                                                                                                                                                         

Options:
 -w [0-99]              = Your warning %. 20 means 20% of your memory can remain before a warning alarm. Do not use the % sign.
 -c [0-99]              = Your critical %. 10 means 10% of your memory can remain before a critical alarm. Do not use the % sign.
 -d [K,M,G,T]   = divider K=kilobytes, M=megabytes, G=gigabytes, T=terabytes
 -f             = Include cached memory as free memory when calculating your percentage free

EOF
        }

invalid_type() {
        echo "\nInvalid $1\n"
        print_help
        exit 3
        }

##############################################
## Suck in the user input
##############################################


while test -n "$1"; do
 case $1 in
  --help) print_help ; exit 0 ;;
  -h) print_help ; exit 0 ;;
  -w) WARN="$2"; shift ;;
  -c) CRIT="$2"; shift ;;
  -d) DIV="$2"; shift ;;
  -f) FC=1 ;;
 esac
 shift
done

##############################################
## Set the defaults if needed
##############################################

[ -z "$WARN" ] && WARN=20
[ -z "$CRIT" ] && CRIT=10
[ -z "$DIV" ] && DIV=M
[ -z "$FC" ] && FC=0

##############################################
## Check user input
##############################################

[ ! -z `echo $WARN | tr -d [:digit:]` ] && invalid_type "Warning: Warning value can only contain numbers"
[ ! -z `echo $CRIT | tr -d [:digit:]` ] && invalid_type "Critical: Critical value can only contain numbers"
[ "${WARN%.*}" -ge 100 ] && invalid_type "Warning: Warning must be smaller than 100%"
[ "${CRIT%.*}" -ge 100 ] && invalid_type "Critical: Critical must be smaller than 100%"
[ "${CRIT%.*}" -gt "${WARN%.*}" ] && invalid_type "Critical: Your Warning must be Higher than your Critical"

case $DIV in
        k|K) DIVNUM=1;;
        m|M) DIVNUM=1024;;
        g|G) DIVNUM=1048576;;
        t|T) DIVNUM=1073741824;;
        *) invalid_type;; 
esac

[ ! -f "$MEMINFO" ] && {
        echo "Your Memory info file seems to be missing"
        exit 1
        }

##############################################
## Do the work
## Pull the memory file into awk
## grab the lines we need
## Print the information
##############################################

RESULT=$(awk -v warn=$WARN -v crit=$CRIT -v div=$DIV -v divnum=$DIVNUM -v fc=$FC '/^MemTotal:/ { total=$2 }
/^MemTotal:/ { tot=$2 }
/^MemFree:/ { free=$2 }
/^Buffers:/ { buff=$2 }
/^Cached:/ { cache=$2 }
/^Active:/ { active=$2 }
/^Inactive:/ { inactive=$2 }
END { if ( fc == 1 ) { free=free+cache+buff }
        { freeperct=free/tot*100 }
        if ( freeperct > warn ) { result="OK" ; xit="0"}
        if ( freeperct <= warn ) {
                if ( freeperct > crit ) { result="WARNING" ; xit="1" }
                else if ( freeperct <= crit ) { result="CRITICAL" ; xit="2" }
                }
        {print xit" MEMORY "result" - "freeperct"% Free - Total:"tot/divnum div" Active:"active/divnum div" Inactive:"inactive/divnum div" Buffers:"buff/divnum div" Cached:"cache/divnum div" |Free="freeperct";"warn";"crit";0 Active="active";0;0;0 Inactive="inactive";0;0;0 Buffers="buff";0;0;0 Cached="cache";0;0;0" }
        }' /proc/meminfo)

echo ${RESULT#* }
exit ${RESULT%% *}





Monitoring der verwendeten Bandbreite

Zum messen der verwendeten Bandbreite verwende ich das Script:


#!/bin/bash

INTERVAL="1"  # update interval in seconds

if [ -z "$1" ]; then
        echo
        echo "example: plugin.sh HOSTNAME system interface_name time warning_mbit/s critical_mbit/s total mbit/s;" 
        echo
        echo "./check_bandwidth.sh localhost linux eth0 15 80 90 100"
        echo "./check_bandwidth.sh switchname cisco GigabitEthernet0/1 15 80 90 100 192.192.192.192 snmp-community"
        exit
fi

name=$1
system=$2
IF=$3
sec=$4
warn=$5
crit=$6
iface_speed=$7
ip=$8
community=$9
current_pid=$$

bin_ps=`which ps`
bin_grep=`which grep`
bin_expr=`which expr`
bin_cat=`which cat`
bin_tac=`which tac`
bin_sort=`which sort`
bin_wc=`which wc`
bin_awk=`which awk`
bin_snmpwalk=`which snmpwalk`
interfaces_oid=1.3.6.1.2.1.2.2.1.2
                                                                                                                                                                        
                                                                                                                                                                        
if [ "$system" = "cisco" ];                                                                                                                                             
    then                                                                                                                                                                
        if_index=`$bin_snmpwalk -c $community -v 2c $ip $interfaces_oid | grep $IF | sed 's/^.*\.//;s/\ .*$//'`
        pidfile=/tmp/"$name"_"$if_index"_check_bandwidth.pid
fi
if [ "$system" = "linux" ];
    then
        pidfile=/tmp/"$name"_"$IF"_check_bandwidth.pid
fi

if [ -f $pidfile ];
    then
        echo "need to reduce the check duration or increase the interval between checks"
        exit 1
    else
        echo $current_pid > $pidfile
fi

if [ "$system" = "linux" ];
    then
        tmpfile_rx=/tmp/"$name"_"$IF"_check_bandwidth_rx.tmp
        tmpfile_tx=/tmp/"$name"_"$IF"_check_bandwidth_tx.tmp
        reverse_tmpfile_rx=/tmp/"$name"_"$IF"_reverse_check_bandwidth_rx.tmp
        reverse_tmpfile_tx=/tmp/"$name"_"$IF"_reverse_check_bandwidth_tx.tmp
        deltafile_rx=/tmp/"$name"_"$IF"_delta_check_bandwidth_rx.tmp
        deltafile_tx=/tmp/"$name"_"$IF"_delta_check_bandwidth_tx.tmp
elif [ "$system" = "cisco" ];
    then
        tmpfile_rx=/tmp/"$name"_"$if_index"_check_bandwidth_rx.tmp
        tmpfile_tx=/tmp/"$name"_"$if_index"_check_bandwidth_tx.tmp
        reverse_tmpfile_rx=/tmp/"$name"_"$if_index"_reverse_check_bandwidth_rx.tmp
        reverse_tmpfile_tx=/tmp/"$name"_"$if_index"_reverse_check_bandwidth_tx.tmp
        deltafile_rx=/tmp/"$name"_"$if_index"_delta_check_bandwidth_rx.tmp
        deltafile_tx=/tmp/"$name"_"$if_index"_delta_check_bandwidth_tx.tmp
        laststate_file=/tmp/"$name"_"$if_index"_laststate.tmp
fi

warn_kbits=`$bin_expr $warn '*' 1000000`
crit_kbits=`$bin_expr $crit '*' 1000000`
iface_speed_kbits=`$bin_expr $iface_speed '*' 1000000`

if [ "$system" = "linux" ];
    then
        START_TIME=`date +%s`
        n=0
        while [ $n -lt $sec ]
            do
                cat /sys/class/net/$3/statistics/rx_bytes >> $tmpfile_rx
                cat /sys/class/net/$3/statistics/tx_bytes >> $tmpfile_tx
                sleep $INTERVAL
                let "n = $n + 1"
            done
        FINISH_TIME=`date +%s`
    $bin_cat $tmpfile_rx | $bin_sort -nr > $reverse_tmpfile_rx
    $bin_cat $tmpfile_tx | $bin_sort -nr > $reverse_tmpfile_tx
    while read line;
        do
            if [ -z "$RBYTES" ];
                then
                    RBYTES=`cat /sys/class/net/$3/statistics/rx_bytes`
                    $bin_expr $RBYTES - $line >> $deltafile_rx;
                else
                    $bin_expr $RBYTES - $line >> $deltafile_rx;
            fi
        RBYTES=$line
        done < $reverse_tmpfile_rx
    while read line;
        do
            if [ -z "$TBYTES" ];
                then
                    TBYTES=`cat /sys/class/net/$3/statistics/tx_bytes`
                    $bin_expr $TBYTES - $line >> $deltafile_tx;
                else
                    $bin_expr $TBYTES - $line >> $deltafile_tx;
            fi
        TBYTES=$line
        done < $reverse_tmpfile_tx
    while read line;
        do
            SUM_RBYTES=`$bin_expr $SUM_RBYTES + $line`
        done < $deltafile_rx
    while read line;
        do
            SUM_TBYTES=`$bin_expr $SUM_TBYTES + $line`
        done < $deltafile_tx
    let "DURATION = $FINISH_TIME - $START_TIME"
    let "RBITS_SEC = ( $SUM_RBYTES * 8 ) / $DURATION"
    let "TBITS_SEC = ( $SUM_TBYTES * 8 ) / $DURATION"
    if [ $RBITS_SEC -lt $warn_kbits  -o  $TBITS_SEC -lt $warn_kbits ]
        then
            data_output_r=`echo "$RBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            data_output_t=`echo "$TBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            percent_output_r=`echo "$RBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            percent_output_t=`echo "$TBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            nagvis_perfdata_r="InUsage=$percent_output_r%;$warn_kbits;$crit_kbits"
            nagvis_perfdata_t="OutUsage=$percent_output_t%;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_r="in=$RBITS_SEC;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_t="in=$TBITS_SEC;$warn_kbits;$crit_kbits"
            output="IN $data_output_r Mbit/s OUT $data_output_t Mbit/s - OK, period $DURATION sec | $nagvis_perfdata_r $nagvis_perfdata_t inBandwidth="$data_output_r"Mbs outBandwidth="$data_output_t"Mbs $pnp4nagios_perfdata_r $pnp4nagios_perfdata_t"
            exitstatus=0
    elif [ $RBITS_SEC -ge $warn_kbits  -a  $RBITS_SEC -le $crit_kbits ] || [ $TBITS_SEC -ge $warn_kbits -a $TBITS_SEC -le $crit_kbits ];
        then
            data_output_r=`echo "$RBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            data_output_t=`echo "$TBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            percent_output_r=`echo "$RBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            percent_output_t=`echo "$TBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            nagvis_perfdata_r="InUsage=$percent_output_r%;$warn_kbits;$crit_kbits"
            nagvis_perfdata_t="OutUsage=$percent_output_t%;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_r="in=$RBITS_SEC;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_t="in=$TBITS_SEC;$warn_kbits;$crit_kbits"
            output="IN $data_output_r Mbit/s OUT $data_output_t Mbit/s WARNING! period $DURATION sec | $nagvis_perfdata_r $nagvis_perfdata_t inBandwidth="$data_output_r"Mbs outBandwidth="$data_output_t"Mbs $pnp4nagios_perfdata_r $pnp4nagios_perfdata_t"
            exitstatus=1
    elif [ $RBITS_SEC -gt $warn_kbits  -o  $TBITS_SEC -gt $warn_kbits ]
        then
            data_output_r=`echo "$RBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            data_output_t=`echo "$TBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            percent_output_r=`echo "$RBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            percent_output_t=`echo "$TBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            nagvis_perfdata_r="InUsage=$percent_output_r%;$warn_kbits;$crit_kbits"
            nagvis_perfdata_t="OutUsage=$percent_output_t%;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_r="in=$RBITS_SEC;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_t="in=$TBITS_SEC;$warn_kbits;$crit_kbits"
            output="IN $data_output_r Mbit/s OUT $data_output_t Mbit/s CRITICAL! period $DURATION sec | $nagvis_perfdata_r $nagvis_perfdata_t inBandwidth="$data_output_r"Mbs outBandwidth="$data_output_t"Mbs $pnp4nagios_perfdata_r $pnp4nagios_perfdata_t"
            exitstatus=2
    else
        output="unknown status"
        exitstatus=3
    fi
elif [ "$system" = "cisco" ];
    then
        START_TIME=`date +%s`
        n=0
        rx_tag=1
        tx_tag=1
        rx_old=0
        tx_old=0
        while [ $n -lt $sec -a $rx_tag -eq 1 -a $tx_tag -eq 1 ];
            do
                rx_now=`$bin_snmpwalk -c $community -v 2c -Oqv $ip 1.3.6.1.2.1.2.2.1.10.$if_index`
                    if [ $rx_now -ge $rx_old ];
                        then 
                        rx_tag=1
                            if [ $rx_now -gt $rx_old ];
                                then
                                    echo $rx_now >> $tmpfile_rx
                            fi
                    else
                        rx_tag=0
                    fi
                rx_old=$rx_now
                tx_now=`$bin_snmpwalk -c $community -v 2c -Oqv $ip 1.3.6.1.2.1.2.2.1.16.$if_index`
                    if [ $tx_now -ge $tx_old ];
                        then 
                        tx_tag=1
                            if [ $tx_now -gt $tx_old ];
                                then
                                    echo $tx_now >> $tmpfile_tx
                            fi
                    else
                        tx_tag=0
                    fi
                tx_old=$tx_now
                sleep $INTERVAL
                let "n = $n + 1"
            done
        FINISH_TIME=`date +%s`
        data_lines_rx=`$bin_cat $tmpfile_rx | $bin_wc -l`
        data_lines_tx=`$bin_cat $tmpfile_tx | $bin_wc -l`
        if [ $data_lines_rx -le 1 -o $data_lines_rx -le 1 ];
            then
                output=`$bin_cat $laststate_file`
                echo $output
                rm -f $tmpfile_rx
                rm -f $tmpfile_tx
                rm -f $pidfile
                exit 0
        fi
        $bin_tac $tmpfile_rx > $reverse_tmpfile_rx
        $bin_tac $tmpfile_tx > $reverse_tmpfile_tx
        while read line;
            do
                if [ -z "$ROCTETS" ];
                    then
                        ROCTETS=$line
                    else
                        $bin_expr $ROCTETS - $line >> $deltafile_rx;
                fi
            ROCTETS=$line
            done < $reverse_tmpfile_rx
        while read line;
            do
                if [ -z "$TOCTETS" ];
                    then
                        TOCTETS=$line
                    else
                        $bin_expr $TOCTETS - $line >> $deltafile_tx;
                fi
            TOCTETS=$line
            done < $reverse_tmpfile_tx
        while read line;
            do
                SUM_ROCTETS=`$bin_expr $SUM_ROCTETS + $line`
            done < $deltafile_rx
        while read line;
            do
                SUM_TOCTETS=`$bin_expr $SUM_TOCTETS + $line`
            done < $deltafile_tx
        let "DURATION = $FINISH_TIME - $START_TIME"
        let "RBITS_SEC = ( $SUM_ROCTETS * 8 ) / $DURATION"
        let "TBITS_SEC = ( $SUM_TOCTETS * 8 ) / $DURATION"
#debug block start
if [ $RBITS_SEC -lt 0 ];
    then
        timestamp=`date +%H%M%S`
        cp $tmpfile_rx "$tmpfile_rx"_"$timestamp"
        cp $reverse_tmpfile_rx "$reverse_tmpfile_rx"_"$timestamp"
        cp $deltafile_rx "$deltafile_rx"_"$timestamp"
fi
if [ $TBITS_SEC -lt 0 ];
    then
        timestamp=`date +%H%M%S`
        cp $tmpfile_tx "$tmpfile_tx"_"$timestamp"
        cp $reverse_tmpfile_tx "$reverse_tmpfile_tx"_"$timestamp"
        cp $deltafile_tx "$deltafile_tx"_"$timestamp"
fi
#debug block finish
    if [ $RBITS_SEC -lt $warn_kbits  -o  $TBITS_SEC -lt $warn_kbits ]
        then
            data_output_r=`echo "$RBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            data_output_t=`echo "$TBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            percent_output_r=`echo "$RBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            percent_output_t=`echo "$TBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            nagvis_perfdata_r="InUsage=$percent_output_r%;$warn_kbits;$crit_kbits"
            nagvis_perfdata_t="OutUsage=$percent_output_t%;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_r="in=$RBITS_SEC;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_t="in=$TBITS_SEC;$warn_kbits;$crit_kbits"
            output="IN $data_output_r Mbit/s OUT $data_output_t Mbit/s - OK, period $DURATION sec | $nagvis_perfdata_r $nagvis_perfdata_t inBandwidth="$data_output_r"Mbs outBandwidth="$data_output_t"Mbs $pnp4nagios_perfdata_r $pnp4nagios_perfdata_t"
            echo $output > $laststate_file
            exitstatus=0
    elif [ $RBITS_SEC -ge $warn_kbits  -a  $RBITS_SEC -le $crit_kbits ] || [ $TBITS_SEC -ge $warn_kbits -a $TBITS_SEC -le $crit_kbits ];
        then
            data_output_r=`echo "$RBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            data_output_t=`echo "$TBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            percent_output_r=`echo "$RBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            percent_output_t=`echo "$TBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            nagvis_perfdata_r="InUsage=$percent_output_r%;$warn_kbits;$crit_kbits"
            nagvis_perfdata_t="OutUsage=$percent_output_t%;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_r="in=$RBITS_SEC;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_t="in=$TBITS_SEC;$warn_kbits;$crit_kbits"
            output="IN $data_output_r Mbit/s OUT $data_output_t Mbit/s WARNING! period $DURATION sec | $nagvis_perfdata_r $nagvis_perfdata_t inBandwidth="$data_output_r"Mbs outBandwidth="$data_output_t"Mbs $pnp4nagios_perfdata_r $pnp4nagios_perfdata_t"
            echo $output > $laststate_file
            exitstatus=1
    elif [ $RBITS_SEC -gt $warn_kbits  -o  $TBITS_SEC -gt $warn_kbits ]
        then
            data_output_r=`echo "$RBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            data_output_t=`echo "$TBITS_SEC 1000000" | $bin_awk '{ printf ("%.2f", $1/$2); }'`
            percent_output_r=`echo "$RBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            percent_output_t=`echo "$TBITS_SEC $iface_speed_kbits 100" | $bin_awk '{ printf ("%.2f", $1/$2*$3); }'`
            nagvis_perfdata_r="InUsage=$percent_output_r%;$warn_kbits;$crit_kbits"
            nagvis_perfdata_t="OutUsage=$percent_output_t%;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_r="in=$RBITS_SEC;$warn_kbits;$crit_kbits"
            pnp4nagios_perfdata_t="in=$TBITS_SEC;$warn_kbits;$crit_kbits"
            output="IN $data_output_r Mbit/s OUT $data_output_t Mbit/s CRITICAL! period $DURATION sec | $nagvis_perfdata_r $nagvis_perfdata_t inBandwidth="$data_output_r"Mbs outBandwidth="$data_output_t"Mbs $pnp4nagios_perfdata_r $pnp4nagios_perfdata_t"
            echo $output > $laststate_file
            exitstatus=2
    else
        output="unknown status"
        exitstatus=3
    fi
else
    output="incorrect system!"
    exitstatus=3
fi

rm -f $tmpfile_rx
rm -f $reverse_tmpfile_rx
rm -f $deltafile_rx
rm -f $tmpfile_tx
rm -f $reverse_tmpfile_tx
rm -f $deltafile_tx
rm -f $pidfile

echo "$output"
exit $exitstatus

Da auf den GW das Problem auftrat, daß der GW keine IP adressen mehr ausgab, und CLients per FAST auf Layer 2 verbunden waren, habe ich die Lease Time auf 30 Minuten eingestellt.

Kontrolle der vergebenen IP-Adressen

Als zusätzliche Kontrolle über die vergebenen IP-Adressen dient das Script:

#!/usr/bin/env python
#
#  Author: Hari Sekhon
#  Date: 2007-11-20 18:16:55 +0000 (Tue, 20 Nov 2007)
#
#  http://github.com/harisekhon
#
#  License: see accompanying LICENSE file
#

"""Nagios plugin to parse the ISC Dhcp Server lease file and print out a list 
of all the Name/IP/MAC associations or any combination of the three. Can also
be used to alert on the delegation of IPs to non-recognized MACs or Hostnames"""

__author__  = "Hari Sekhon"
__title__   = "Nagios Plugin for DHCPd Server Leases"
__version__ = 0.8

# Due to the limited of characters that Nagios accepts from a plugin, this 
# output will be cut short if you have a lot of dhcp clients, which is why
# the -c switch was included to compact the output to fit more in.

# Remember, this program can be used without Nagios so that character limit 
# need not be your limit.

import re
import sys
import signal
from optparse import OptionParser

# Standard Nagios return codes
OK       = 0
WARNING  = 1
CRITICAL = 2
UNKNOWN  = 3

# Default timeout. All good plugins should have a timeout to prevent hanging
TIMEOUT  = 30

# Regex for lease comparison and dissection
RE_BINDING_STATE_ACTIVE = re.compile("\s*binding state active;")
RE_LEASE                = re.compile("lease")
RE_HOSTNAME             = re.compile("client-hostname")
RE_MAC                  = re.compile("hardware\sethernet")
RE_IP_ADDRESS           = re.compile("(\d{1,3}.){3}\d{1,3}")

def end(status, message):
    """Exits the plugin with first arg as the return code and the second
    arg as the message to output"""
        
    if status == OK:
        print "DHCP LEASES: %s" % message
        sys.exit(OK)
    elif status == WARNING:
        print "WARNING: %s" % message
        sys.exit(WARNING)
    elif status == CRITICAL:
        print "CRITICAL: %s" % message
        sys.exit(CRITICAL)
    else:
        print "UNKNOWN: %s" % message
        sys.exit(UNKNOWN)


def sort_keys_by_host(dictionary):
    """Takes the address dictionary in the form {"ip":["hostname","mac"]},
    sorts the keys by the host value, returns an ordered list of keys"""
    
    hosts = []
    keys = dictionary.keys()
    keys.sort()
    keys_sorted = []
    for key in keys:
        hosts.append(dictionary[key][0])
    hosts.sort()
    for host in hosts:
        for key in keys:
            if dictionary[key][0] == host:
                keys_sorted.append(key)

    # ((k, v) for k, v in mydict), key=lambda i: i[1])
    
    # dedup without losing order
    keys_sorted2 = []
    for key in keys_sorted:
        if key not in keys_sorted2:
            keys_sorted2.append(key)
    keys_sorted = keys_sorted2

    return keys_sorted


class DhcpdLeaseTester:
    """Class to hold all Dhcpd Lease test state"""

    def __init__(self):
        """Instantiate variables"""

        self.address_dict      = {}
        self.compact_output    = False
        self.host_whitelist    = ""
        self.host_blacklist    = ""
        self.leasefile         = None
        self.lease             = ""
        self.leases            = []
        self.mac_whitelist     = ""
        self.mac_blacklist     = ""
        self.no_name           = False
        self.no_summary        = False
        self.show_mac          = False
        self.sort_by_ip        = False
        self.timeout           = TIMEOUT
        self.unauthorized      = False
        self.unauthorized_dict = {}

    def validate_variables(self):
        """Validates all variables as defined in self.__init__"""

        if self.compact_output == None:
            self.compact_output = False
        if self.host_whitelist == None:
            self.host_whitelist = ""
        if self.host_blacklist == None:
            self.host_blacklist = ""
        if self.leasefile == None:
            end(UNKNOWN, "No leasefile to test")
        if self.mac_whitelist == None:
            self.mac_whitelist = ""
        if self.mac_blacklist == None:
            self.mac_blacklist = ""

        if self.timeout == None:
            self.timeout = TIMEOUT

        self.validate_normalize_macs("whitelist") 
        self.validate_normalize_macs("blacklist") 

        try:
            self.timeout = int(self.timeout)
        except ValueError:
            end(UNKNOWN, "timeout invalid, must be a numeric integer")

    
    def validate_normalize_macs(self, colourlist):
        """Checks to make sure any Mac addresses given 
        are in the correct format. Takes either whitelist or blacklist
        and then validates each mac in that list and changes the list
        to a normalized uppercase hex mac with no formatting or
        separators"""

        maclist = getattr(self, "mac_" + colourlist)
        maclist = maclist.replace(",", " ")
        maclist = maclist.split()
        maclist = [mac.replace(":","") for mac in maclist]
        maclist = [mac.upper() for mac in maclist]

        re_mac_format = re.compile("^([\dA-Fa-f]{2}[:-]?){5}[\dA-Fa-f]{2}$")

        for mac in maclist:
            if not re_mac_format.match(mac):
                end(UNKNOWN, "'%s' was given as a Mac address but is " % mac
                           + "not a valid Mac")

        setattr(self, "mac_" + colourlist, maclist)


    def sighandler(self, discarded, discarded2):
        """Function to be called by signal.alarm to kill the plugin"""

        # Nop for these variables
        discarded = discarded2
        discarded2 = discarded

        end(CRITICAL, "plugin has self terminated after exceeding " \
                    + "the timeout (%s seconds)" % self.timeout)


    def test_leases(self):
        """Initiates the test, calls parse_leases and possibly the condition
        checking funcs too if whitelist or blacklists are used"""

        self.validate_variables()

        signal.signal(signal.SIGALRM, self.sighandler)
        signal.alarm(self.timeout)

        # In the format {"ip":["hostname","mac"]}
        self.address_dict = {}

        result = OK

        try:
            output = self.parse_leases()
        except IndexError:
            end(CRITICAL, "Error parsing dhcp leases file '%s', possibly not " \
                        + "valid lease file?" % self.leasefile)

        self.check_unauthorized_leases()

        if self.unauthorized == True:
            unauthorized_output = self.format_unauthorized_leases()
            output = unauthorized_output + " || " + output
            result = CRITICAL

        # When we used to compact the whitespace out, not used any more
        #if self.compact_output:
        #    output = output.replace(" - ", "-")
        #    output = output.replace(" = ", "=")
        #    output = output.replace(", ", ",")
        #    output = output.replace(" (", "(")
        #    output = output.replace(" | ", "|")

        return result, output


    def parse_leases(self):
        """Parse leases file and returns a string of leases with IP addreses
        and optional Hostname/Mac mappings"""

        self.leases = self.open_lease_file()

        security_checks_on = False
        if self.host_whitelist or \
           self.host_blacklist or \
           self.mac_whitelist  or \
           self.mac_blacklist:
            security_checks_on = True

        for self.lease in self.leases:
            if RE_BINDING_STATE_ACTIVE.search(self.lease):
                hostname = ""
                ip       = ""
                mac      = ""

                self.lease = self.lease.split("\n")

                ip = self.get_ip()
                # If this is not valid, there can be no lease so move on
                # This actually catches things like the user not using a decent
                # lease file, in which case this will result in no real leases
                # and will therefore result in a true result of no leases.
                # Actually forcing the correct lease file turned out to not
                # be fully possible since it varies among servers, so this
                # catches the rest
                if ip == "UNKNOWN":
                    continue
                self.address_dict[ip] = ["Unknown Hostname", "Unknown Mac"]
                if not self.no_name or security_checks_on:
                    hostname = self.get_hostname()
                    self.address_dict[ip][0] = hostname

                if self.show_mac == True or security_checks_on:
                    mac = self.get_mac()
                    self.address_dict[ip][1] = mac

        msg = self.format_leases()

        return msg


    def check_unauthorized_leases(self):
        """Checks for and call functions to test the leases against the given
        whitelists/blacklists. Returns a list where the first element is a True
        or False overall result, and the second element is a dictionary of 
        offending hosts with 'mac' and 'host' keys representing an array of
        hosts that tripped each type of rule"""

        if self.mac_whitelist:
            self.check_mac_whitelist()

        if self.mac_blacklist:
            self.check_mac_blacklist()
            
        if self.host_whitelist:
            self.check_host_whitelist()
            
        if self.host_blacklist:
            self.check_host_blacklist()
           
        if len(self.unauthorized_dict) > 0:
            self.unauthorized = True


    def check_host_whitelist(self):
        """Checks the self.address_dict for any hostname not in the host
        whitelist and returns a list of unauthorized hostnames"""
       
        host_whitelist = self.host_whitelist.replace(",", " ")
        host_whitelist = host_whitelist.split()
        host_whitelist = [host.upper() for host in host_whitelist]

        for ip in self.address_dict.keys():
            hostname = self.address_dict[ip][0]
            mac      = self.address_dict[ip][1]
            if hostname.upper() not in host_whitelist:
                self.unauthorized_dict[ip] = (hostname, mac)


    def check_host_blacklist(self):
        """Checks the self.address_dict for any hostname not in the host
        blacklist and returns a list of unauthorized hostnames"""
       
        host_blacklist = self.host_blacklist.replace(",", " ")
        host_blacklist = host_blacklist.split()
        host_blacklist = [host.upper() for host in host_blacklist]

        for ip in self.address_dict.keys():
            hostname = self.address_dict[ip][0]
            mac      = self.address_dict[ip][1]
            if hostname.upper() in host_blacklist:
                self.unauthorized_dict[ip] = (hostname, mac)


    def check_mac_whitelist(self):
        """Checks the self.address_dict for any macname not in the mac
        whitelist and returns a list of unauthorized macnames"""
       
        for ip in self.address_dict.keys():
            hostname = self.address_dict[ip][0]
            mac      = self.address_dict[ip][1]
            mac      = mac.replace(":", "")
            mac      = mac.upper()
            if mac not in self.mac_whitelist:
                self.unauthorized_dict[ip] = (hostname, mac)


    def check_mac_blacklist(self):
        """Checks the self.address_dict for any macname not in the mac
        blacklist and returns a list of unauthorized macnames"""
       
        for ip in self.address_dict.keys():
            hostname = self.address_dict[ip][0]
            mac      = self.address_dict[ip][1]
            mac      = mac.replace(":", "")
            mac      = mac.upper()
            if mac in self.mac_blacklist:
                self.unauthorized_dict[ip] = (hostname, mac)


    def format_leases(self):
        """Takes the address dictionary in the form {"ip":("hostname","mac")}
        and formats the output, returns a string."""

        if self.no_name == True:
            self.sort_by_ip = True
        
        if self.sort_by_ip == True:
            address_keys_sorted = self.address_dict.keys()
            address_keys_sorted.sort()
        else:
            address_keys_sorted = sort_keys_by_host(self.address_dict)

        number_leases = len(address_keys_sorted)
        if number_leases == 0:
            msg = "No dhcp leases recorded"
        else:
            if self.no_summary:
                msg = ""
            else:
                if number_leases == 1:
                    msg = "%s lease" % number_leases
                else:
                    msg = "%s leases" % number_leases
                if not self.compact_output:
                    msg += " - "
            if not self.compact_output:
                for ip in address_keys_sorted:
                    if self.no_name == True:
                        msg += "%s" % ip
                    else:
                        hostname = self.address_dict[ip][0]
                        msg += "%s = %s" % (hostname, ip)
                    if self.show_mac:
                        mac = self.address_dict[ip][1]
                        msg += " (%s)" % mac
                    msg += ", "
                msg = msg.rstrip(", ")

            msg += " | 'DHCP Leases'=%s" % number_leases

        return msg


    def format_unauthorized_leases(self):
        """Takes the self unauthorized dictionary in the form 
        {"ip":("hostname","mac","offending")}
        and formats the output, returns a string."""

        if self.no_name == True:
            self.sort_by_ip = True
        
        if self.sort_by_ip == True:
            unauthorized_keys_sorted = self.unauthorized_dict.keys()
            unauthorized_keys_sorted.sort()
        else:
            unauthorized_keys_sorted = sort_keys_by_host(self.unauthorized_dict)

        number_unauthorized = len(unauthorized_keys_sorted)
        if number_unauthorized == 0:
            return ""
        else:
            if number_unauthorized == 1:
                msg = "%s Unauthorized Host! " % number_unauthorized
            else:
                msg = "%s Unauthorized Hosts! " % number_unauthorized
            for ip in unauthorized_keys_sorted:
                if self.no_name == True:
                    msg += "%s" % ip
                else:
                    hostname = self.unauthorized_dict[ip][0]
                    msg += "%s = %s" % (hostname, ip)
                if self.show_mac:
                    # Get original instead as Mac has changed by this point
                    # This maintains better output consistency but relies
                    # on the leases file being slightly sane..., but then
                    # if your leases file is not sane, how is that my fault?
                    # This would be a failing of dhcpd more than this plugin
                    #mac = self.unauthorized_dict[ip][1]
                    mac = self.address_dict[ip][1]
                    msg += " (%s)" % mac
                msg += ", "

            msg = msg.rstrip(", ")

        return msg


    def open_lease_file(self):
        """Opens the lease file, tests the lease file is valid, and then returns
        an array of elements each containing one lease block definition"""
        
        leases_array = []

        try:
            file_handle = open(self.leasefile)
            leases      = file_handle.read()
        except IOError:
            end(CRITICAL, "Error reading lease file '%s'" % self.leasefile)

        # Check to see if it is a valid lease file.
        # If there are no leases, then we should check that there are some
        # header keywords comment that you usually seen in a dhcpd.leases file
        
        # This isn't really good enough but otherwise it can break across
        # different systems. Looser than I would like but the user should 
        # really be using a valid lease file. Parse leases will also catch
        # this in that no leases will be created and the result will be
        # technically true, there are no valid leases in an incorrect file
        if not re.search("\n\s*lease .+{\s*\n", leases) \
            and not \
                re.search("(?i)\n#.*lease[s]? file .* written by.*\n", leases) \
            and not re.search("\n#.*dhcpd.leases.*\n", leases) \
            and not re.search("\n(?i)#.*isc-dhcp.*\n", leases):
            end(CRITICAL, "'%s' is not recognized as a valid dhcpd lease file" \
                                                               % self.leasefile)

        leases = leases.split("}")[:-1]
        for lease in leases:
            lease_parts = lease.split("{")
            if len(lease_parts) > 1:
                leases_array.append(lease_parts[1])
            #else:
            #    print >> sys.stderr, "Debug - Not valid lease:\n%s" % lease

        return leases


    def get_ip(self):
        """Takes an list of lines from a dhcp lease block from self.leases
        and returns the ip address of this lease"""

        ip = ""

        for line in self.lease:
            if RE_LEASE.search(line):
                line = line.split()
                if len(line) == 3:
                    ip = line[1]
                else:
                    ip = "UNKNOWN"
        
        if not RE_IP_ADDRESS.match(ip):
            ip = "UNKNOWN"

        return ip 


    def get_hostname(self):
        """Takes an list of lines from a dhcp lease block from self.leases
        and returns the hostname of the machine with this lease"""

        hostname = ""

        for line in self.lease:
            if RE_HOSTNAME.search(line):
                line = line.split()
                if len(line) == 2:
                    hostname = line[1]
                    hostname = hostname.rstrip(";")
                    hostname = hostname.strip('"')
                else:
                    hostname = "UNKNOWN"

        if hostname == "" or hostname == None:
            hostname = "UNKNOWN"

        return hostname 


    def get_mac(self):
        """Takes an list of lines from a dhcp lease block from self.leases
        and returns the Mac of the machine with this lease"""

        mac = ""

        for line in self.lease:
            if RE_MAC.search(line):
                line = line.split()
                if len(line) == 3:
                    mac = line[2]
                    mac = mac.rstrip(";")
                else:
                    mac = "UNKNOWN"

        if mac == "" or mac == None:
            mac = "UNKNOWN"

        return mac


def main():
    """Main func parses command line args and calls print_leases"""

    tester = DhcpdLeaseTester()
    parser = OptionParser()
    parser.add_option( "-c",
                       "--compact-output",
                       action="store_true",
                       dest="compact_output",
                       help="Compact the output, do not list leases. Use this" \
                          + "to make sure Nagios gets perfdata as NRPE has" \
                          + " a limit on the number of characters before it "  \
                          + "discards the rest")
    parser.add_option( "-f",
                       "--file",
                       "--lease-file",
                       dest="leasefile",
                       help="Specify the dhcp lease file to use. Should be "  \
                          + "the current lease file that the ISC dhcp "       \
                          + "daemon uses to track it's leases")
    parser.add_option( "-m",
                       "--mac",
                       action="store_true",
                       dest="show_mac",
                       help="Show mac addresses as well as Name/IP pairings")
    parser.add_option( "-n",
                       "--no-name",
                       action="store_true",
                       dest="no_name",
                       help="Do not display hostnames. When used by itself, "  \
                          + "this just shows assigned IP addresses. Can be "   \
                          + "used in conjunction with --mac in order to show " \
                          + "only IP/Mac pairings")
    parser.add_option( "-i",
                       "--sort-by-ip",
                       action="store_true",
                       dest="sort_by_ip",
                       help="Change the output order to sort by IP rather "    \
                          + "than the default of sorting by hostname. If "     \
                          + "using --no-name this is implied")
    parser.add_option( "-s",
                       "--no-summary",
                       action="store_true",
                       dest="no_summary",
                       help="Do not print the summary of the number of dhcp "  \
                          + "leases used")
    parser.add_option( "-t",
                       "--timeout",
                       dest="timeout",
                       help="Timeout in seconds before the plugin self "       \
                          + "terminates. This should never be needed but the " \
                          + "Nagios coding guidelines recommend it and "       \
                          + "therefore it is implemented for completeness. "   \
                          + "Use this to specify a custom timeout period in "  \
                          + "seconds (should be an integer/whole number). "    \
                          + "Defaults to %s seconds" % TIMEOUT)
    parser.add_option( "-w",
                       "--host-whitelist",
                       dest="host_whitelist",
                       help="Whitelist of known Hostnames. Raises alert"       \
                          + " if an IP has been issued to any machine with a " \
                          + "Hostname not in this list. Considered weak since" \
                          + " the hostname can be set on the client machine "  \
                          + "before requesting a dhcp lease. But it's there "  \
                          + "if you want it. Can be a nice extra layer to the" \
                          + " defense in depth strategy when properly used "   \
                          + "with a Mac whitelist as well. Although Mac "      \
                          + "addresses can also be spoofed, some attackers "   \
                          + "may not think to spoof the hostname as well as"   \
                          + " the mac address. Should be a comma or space "    \
                          + "separated list, enclosed in quotes if using "     \
                          + "spaces. Hostnames are case insensitive")
    parser.add_option( "-x",
                       "--host-blacklist",
                       dest="host_blacklist",
                       help="Blacklist of known Hostnames. Raises "            \
                          + "alert if an IP has been handed out to a machine " \
                          + "with this Hostname. Can take a list of Hostnames" \
                          + ", comma or space separated (enclose in quotes if" \
                          + " using spaces). Can be combined with any "        \
                          + "Whitelist, in which case, blacklists always take" \
                          + " preference over whitelists and raise an alert. " \
                          + "Hostnames are case insensitive")
    parser.add_option( "-y",
                       "--mac-whitelist",
                       dest="mac_whitelist",
                       help="Whitelist of known Mac addresses. Raises "        \
                          + "alert if an IP has been issued to any machine "   \
                          + "with a Mac address not in this list. Although "   \
                          + "Mac addresses can be spoofed, this may not have " \
                          + "been done when requesting the dhcp lease. For "   \
                          + "extra layers combine with --host-whitelist to "   \
                          + "form a nice additional tripwire. Should be a "    \
                          + "comma or space separated list, enclosed in "      \
                          + "quotes if using spaces. Valid Mac formats: "      \
                          + "aa:bb:cc:dd:ee:ff, or aa-bb-cc-dd-ee-ff or "      \
                          + "aabbccddeeff (case insensitive)")
    parser.add_option( "-z",
                       "--mac-blacklist",
                       dest="mac_blacklist",
                       help="Blacklist of known Mac addresses. Raises "        \
                          + "alert if an IP has been handed out to a machine " \
                          + "with this Mac address. Can take a list of Macs"   \
                          + ", comma or space separated (enclose in quotes if" \
                          + " using spaces). Can be combined with any "        \
                          + "Whitelist, in which case, blacklists always take" \
                          + " preference over whitelists and raise an alert. " \
                          + "Valid Mac formats: aa:bb:cc:dd:ee:ff, or "        \
                          + "aa-bb-cc-dd-ee-ff or aabbccddeeff (case "         \
                          + "insensitive)")
    parser.add_option( "-V",
                        "--version",
                        action = "store_true",
                        dest = "version",
                        help = "Print version number and exit" )

    (options, args) = parser.parse_args()


    tester.compact_output = options.compact_output
    tester.host_whitelist = options.host_whitelist
    tester.host_blacklist = options.host_blacklist
    tester.leasefile      = options.leasefile
    tester.mac_whitelist  = options.mac_whitelist
    tester.mac_blacklist  = options.mac_blacklist
    tester.no_name        = options.no_name
    tester.no_summary     = options.no_summary
    tester.show_mac       = options.show_mac
    tester.sort_by_ip     = options.sort_by_ip

    timeout               = options.timeout
    version               = options.version

    if args:
        parser.print_help()
        sys.exit(UNKNOWN)

    if version:
        print __version__
        sys.exit(UNKNOWN)

    if not tester.leasefile:
        print "UNKNOWN: no lease file specified. See --help for details\n"
        parser.print_help()
        sys.exit(UNKNOWN)

    if timeout == None:
        timeout = TIMEOUT

    try:
        tester.timeout = int(timeout)
    except ValueError:
        end(UNKNOWN, "timeout must be a numeric integer. See --help for " \
                   + "details")

    result, output = tester.test_leases()

    end(result, output)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print "Caught Control-C..."
        sys.exit(UNKNOWN)


Script für die Anzeige

Zur Anzeige brauche ich dann noch folgendes Script:


! /bin/sh

#result=$(sudo /usr/local/sbin/batctl tg | grep '^ [^ ]' | cut -b 39-55 | sort | uniq | wc -l)
#result=$(sudo /usr/local/sbin/batctl tg | grep '^ [^ ]' | cut -b 39-55 | sort | uniq | wc -l)

add_perf=$result
#warn=500
warn=50
#crit=600
crit=60
count=30000
#x=$(($count / $result))


#echo "Connection OK $result|$x KBit/sec;Connections=${result};${warn},${crit};;"
#echo "Connection OK $result Connections $x kbit/client |$x;${warn},${crit};;"


# #####################################################################
# Add SERVICEPERFDATA
# format: 'label'=value[unit-of-measure];[warn];[crit];[min];[max]
# #####################################################################
#OUTPUT="$OUTPUT|banned_IP=${bcount};${warn};${crit};;"

result=$(/usr/lib/nagios/plugins/check_dhcpd_leases.py -c -n -f /var/lib/dhcp/dhcpd.leases)
echo "Verbindungen OK $result"


exit 0