diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a01ee289 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.*.swp diff --git a/images/README.md b/images/README.md index fedf12ba..9216f9b0 100644 --- a/images/README.md +++ b/images/README.md @@ -1,2 +1,4 @@ Please **don't** put screenshots of your plugins here. Put them right next to your plugins. + +The `t-shirt/` subdir contains a t-shirt vector gfx. diff --git a/images/t-shirts/README.md b/images/t-shirts/README.md new file mode 100644 index 00000000..ae8aa3a4 --- /dev/null +++ b/images/t-shirts/README.md @@ -0,0 +1,7 @@ +# T-Shirt subproject + +Some t-shirts + +![logo-horizontal-tshirt-black-modified.svg](logo-horizontal-tshirt-black-modified.svg "logo-horizontal-tshirt-black-modified.svg") +![logo-shirt-black.svg](logo-shirt-black.svg "logo-shirt-black.svg") +![logo-horizontal-tshirt-black.svg](logo-horizontal-tshirt-black.svg "logo-horizontal-tshirt-black.svg") diff --git a/images/t-shirts/logo-horizontal-tshirt-black-modified.svg b/images/t-shirts/logo-horizontal-tshirt-black-modified.svg new file mode 100644 index 00000000..f0e0c683 --- /dev/null +++ b/images/t-shirts/logo-horizontal-tshirt-black-modified.svg @@ -0,0 +1,75 @@ + + + + + + + www.munin-monitoring.org + + + + + + + + + + + + + + + + diff --git a/images/t-shirts/logo-horizontal-tshirt-black.svg b/images/t-shirts/logo-horizontal-tshirt-black.svg new file mode 100644 index 00000000..3ce5c0e6 --- /dev/null +++ b/images/t-shirts/logo-horizontal-tshirt-black.svg @@ -0,0 +1,172 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + www.munin-monitoring.org + + + + + + + + + + + + + + + + diff --git a/images/t-shirts/logo-shirt-black.svg b/images/t-shirts/logo-shirt-black.svg new file mode 100644 index 00000000..6b2d8289 --- /dev/null +++ b/images/t-shirts/logo-shirt-black.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk new file mode 100755 index 00000000..7a3c13ce --- /dev/null +++ b/plugins/asterisk/asterisk @@ -0,0 +1,378 @@ +#!/usr/bin/perl -w +# -*- cperl -*- + +=head1 NAME + +asterisk - Multigraph-capable plugin to monitor Asterisk + +=head1 NOTES + +This plugin will produce multiple graphs showing: + + - total number of active channels (replaces asterisk_channels), + together with breakdown of specific channel types (replaces + asterisk_channelstypes); + + - the number of messages in all voicemail boxes (replaces + asterisk_voicemail); + + - the number of active MeetMe conferences and users connected to them + (replace asterisk_meetme and asterisk_meetmeusers, respectively); + + - the number of active channels for a given codec, for both SIP and + IAX2 channels (replaces asterisk_sipchannels and asterisk_codecs). + +=head1 CONFIGURATION + +The following configuration parameters are used by this plugin + + [asterisk] + env.host - hostname to connect to + env.port - port number to connect to + env.username - username used for authentication + env.secret - secret used for authentication + env.channels - The channel types to look for + env.codecsx - List of codec IDs (hexadecimal values) + env.codecs - List of codecs names, matching codecsx order + +The "username" and "secret" parameters are mandatory, and have no +defaults. + +=head2 DEFAULT CONFIGURATION + + [asterisk] + env.host 127.0.0.1 + env.port 5038 + env.channels Zap IAX2 SIP + env.codecsx 0x2 0x4 0x8 + env.codecs gsm ulaw alaw + +=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: + + [asterisk_someserver] + env.host someserver + env.port 5038 + +=head1 AUTHOR + +Copyright (C) 2005-2006 Rodolphe Quiedeville +Copyright (C) 2012 Diego Elio Pettenò + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +use strict; +use Munin::Plugin; +use IO::Socket; + +sub asterisk_command { + my ($socket, $command) = @_; + my $line, my $reply; + + $socket->print("Action: command\nCommand: $command\n\n"); + + # Response: (Error|Follows|???) + $line = $socket->getline; + if ($line ne "Response: Follows\r\n") { + while ( $line = $socket->getline and $line ne "\r\n" ) {} + return undef; + } + + # Privilege: Command + $line = $socket->getline; + + # Until we get the --END COMMAND-- marker, it's the command's output. + while ( $line = $socket->getline and $line ne "--END COMMAND--\r\n" ) { + $reply .= $line; + } + + # And then wait for the empty line that says we're done + while ( $line = $socket->getline and $line ne "\r\n" ) {} + + return $reply; +} + +$0 =~ /asterisk(?:_(.+))$/; +my $hostname = $1; + +my $peeraddr = $ENV{'host'} || $hostname || '127.0.0.1'; +my $peerport = $ENV{'port'} || '5038'; + +my $username = $ENV{'username'}; +my $secret = $ENV{'secret'}; + +my @CHANNELS = exists $ENV{'channels'} ? split ' ',$ENV{'channels'} : qw(Zap IAX2 SIP); +my @CODECS = exists $ENV{'codecs'} ? split ' ',$ENV{'codecs'} : qw(gsm ulaw alaw); +my @CODECSX = exists $ENV{'codecsx'} ? split ' ',$ENV{'codecsx'} : qw(0x2 0x4 0x8); + +my $line, my $error; +my $socket = new IO::Socket::INET(PeerAddr => $peeraddr, + PeerPort => $peerport, + Proto => 'tcp') + or $error = "Could not create socket: $!"; + +if ( $socket ) { + # This will consume the "Asterisk Call Manager" welcome line. + $socket->getline; + + $socket->print("Action: login\nUsername: $username\nSecret: $secret\nEvents: off\n\n"); + my $response_status = $socket->getline; + + if ( $response_status ne "Response: Success\r\n" ) { + my $response_message = $socket->getline; + $response_message =~ s/Message: (.*)\r\n/$1/; + $error = "Asterisk authentication error: " . $response_message; + } + + while ( $line = $socket->getline and $line ne "\r\n" ) {} +} + +if ( $ARGV[0] and $ARGV[0] eq 'autoconf' ) { + if ( $error ) { + print "no ($error)\n"; + } else { + print "yes\n"; + } + + exit 0; +} elsif ( $ARGV[0] and $ARGV[0] eq 'config' ) { + print "host_name $hostname" if $hostname; + + print <close(); + +my $active_channels = 'U'; +$active_channels = $1 if $channels_response =~ /\n([0-9]+) active channels/; + +print < $HOME/run/$(basename $0).txt + +# Get users list +touch $HOME/run/$(basename $0).users +awk '{ print $2 }' $HOME/run/$(basename $0).txt | cat $HOME/run/$(basename $0).users - | sort -ru > $HOME/run/$(basename $0).users.tmp +mv $HOME/run/$(basename $0).users.tmp $HOME/run/$(basename $0).users + +# Emit config +if [ "$1" = "config" ] +then + awk ' { print $1 ".label " $1 "\n" $1 ".draw AREASTACK" }' $HOME/run/$(basename $0).users +fi + +# Emit values +for i in $( cat $HOME/run/$(basename $0).users ) +do + TMPLINE=$(awk -v i=$i '($2 == i) { print }' $HOME/run/$(basename $0).txt) + if [ -z "$TMPLINE" ] + then + echo "$i.value 0" + else + echo "$TMPLINE" | awk ' { print $2 ".value " $1 }' + + fi +done diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index a43f5989..453c50fa 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -180,7 +180,7 @@ case $name in totalMemGpu=`echo "$totalMemGpus" | sed -n $(( $nGpusCounter + 1 ))p` usedMemGpu=`echo "$usedMemGpus" | sed -n $(( $nGpusCounter + 1 ))p` percentMemUsed=$(( $usedMemGpu * 100 / $totalMemGpu )) - valueGpus="${valueGpus}${percentMemUsed}\n" + valueGpus="${valueGpus}${percentMemUsed}" : $(( nGpusCounter = $nGpusCounter + 1 )) done ;; diff --git a/plugins/http/http_request_time b/plugins/http/http_request_time index 509db0d8..8ecd774d 100755 --- a/plugins/http/http_request_time +++ b/plugins/http/http_request_time @@ -10,7 +10,12 @@ =head1 CONFIGURATION [http_request_time] - env.url http://127.0.0.1/1 http://127.0.0.1/2 http://127.0.0.1/3 + env.url1 http://127.0.0.1/1 + env.url2 http://127.0.0.1/2 + env.url3 http://www.example.com + env.url3_name some_munin_internal_name + env.url3_label Some random page on our website + env.url3_proxy http://firewall:3128 =head1 MAGIC MARKERS @@ -41,7 +46,6 @@ sub clean { return $surl; }; - if (! eval "require LWP::UserAgent;") { $ret = "LWP::UserAgent not found"; @@ -50,12 +54,19 @@ if (! eval "require LWP::UserAgent;") } } -my $URL = $ENV{'url'}?$ENV{'url'}:"http://127.0.0.1/"; my %URLS; -foreach $_ (split(/ /,$URL)){ - $URLS{$_}={ - url=>$_, - surl=>clean($_), + +for (my $i = 1; $ENV{"url$i"}; $i++) +{ + my $url = $ENV{"url$i"}; + my $proxy = $ENV{"url${i}_proxy"}; + my $name = $ENV{"url${i}_name"} || clean($url); + my $label = $ENV{"url${i}_label"} || $url; + + $URLS{$name}={ + url=>$url, + proxy=>$proxy, + label=>$label, time=>'U' }; } @@ -94,50 +105,67 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) print "graph_category other\n"; my @go; - foreach my $url (values %URLS) { - print "$$url{'surl'}.label $$url{'url'}\n"; - print "$$url{'surl'}.info The response time of a single request\n"; - print "$$url{'surl'}.min 0\n"; - print "$$url{'surl'}.draw LINE1\n"; - push(@go,$$url{'surl'}); + foreach my $name (keys %URLS) { + my $url = $URLS{$name}; + print "$name.label $$url{'label'}\n"; + print "$name.info The response time of a single request\n"; + print "$name.min 0\n"; + print "$name.draw LINE1\n"; + push(@go, $name); } # multigraphs - foreach my $url (values %URLS) { - print "\nmultigraph http_request_time.$$url{'surl'}\n"; + foreach my $name (keys %URLS) { + my $url = $URLS{$name}; + print "\nmultigraph http_request_time.$name\n"; print "graph_title $$url{'url'}\n"; print "graph_args --base 1000\n"; print "graph_vlabel response time in ms\n"; print "graph_category other\n"; - print "$$url{'surl'}.label $$url{'url'}\n"; - print "$$url{'surl'}.info The response time of a single request\n"; - print "$$url{'surl'}.min 0\n"; - print "$$url{'surl'}.draw LINE1\n"; + print "$name.label $$url{'label'}\n"; + print "$name.info The response time of a single request\n"; + print "$name.min 0\n"; + print "$name.draw LINE1\n"; } exit 0; } my $ua = LWP::UserAgent->new(timeout => 15); +foreach my $name (keys %URLS) { + my $url = $URLS{$name}; -foreach my $url (values %URLS) { - my $t1=[gettimeofday]; + if ($url->{proxy}) { + $ua->proxy(['http', 'ftp'], $url->{proxy}); + } + else { + $ua->proxy(['http', 'ftp'], undef); + } + + # warm up my $response = $ua->request(HTTP::Request->new('GET',$$url{'url'})); + + # timed run + my $t1=[gettimeofday]; + $response = $ua->request(HTTP::Request->new('GET',$$url{'url'})); my $t2=[gettimeofday]; + if ($response->is_success) { $$url{'time'}=sprintf("%d",tv_interval($t1,$t2)*1000); }; }; print("multigraph http_request_time\n"); -foreach my $url (values %URLS) { - print("$$url{'surl'}.value $$url{'time'}\n"); -} -foreach my $url (values %URLS) { - print("\nmultigraph http_request_time.$$url{'surl'}\n"); - print("$$url{'surl'}.value $$url{'time'}\n"); +foreach my $name (keys %URLS) { + my $url = $URLS{$name}; + print("$name.value $$url{'time'}\n"); } +foreach my $name (keys %URLS) { + my $url = $URLS{$name}; + print("\nmultigraph http_request_time.$name\n"); + print("$name.value $$url{'time'}\n"); +} # vim:syntax=perl diff --git a/plugins/mail/qmailconn b/plugins/mail/qmailconn index 65413ef2..eb4fbf9e 100755 --- a/plugins/mail/qmailconn +++ b/plugins/mail/qmailconn @@ -2,7 +2,11 @@ # # Plugin to show amount of smtp-connections per hour # -# Contributed by Håkon Nessjøen +# Contributed by HÃ¥kon Nessjøen +# +# Modified by Massimiliano Cianelli +# 2013-02-02 - Added support for Greylist and simscan Virus\Spam detect +# Added LOGPATH env var # # Magic markers - optional - used by installation scripts and # munin-config: @@ -10,6 +14,9 @@ #%# family=manual #%# capabilities=autoconf +# get: env.logpath +LOGPATH=${logpath:-/var/log/qmail/qmail-smtpd/} + if [ "$1" = "autoconf" ]; then echo yes exit 0 @@ -17,26 +24,41 @@ fi if [ "$1" = "config" ]; then - echo 'graph_title Qmail SMTP connections' - echo 'graph_args --base 1000 -l 0 ' - echo 'graph_vlabel connections/hour' - echo 'graph_category Mail' - echo 'graph_order rbl accepted total' - echo 'rbl.label RBL rejected connections' - echo 'rbl.min 0' - echo 'rbl.draw AREA' - echo 'accepted.label Accepted connections' - echo 'accepted.min 0' - echo 'accepted.draw STACK' - echo 'total.label Total connections' - echo 'total.min 0' - echo 'total.draw LINE1' + echo 'graph_title Qmail SMTP connections' + echo 'graph_args --base 1000 -l 0 ' + echo 'graph_vlabel connections/hour' + echo 'graph_category mail' + echo 'graph_order rbl greylisted accepted simscan_spam simscan_virus total' + echo 'rbl.label RBL rejected connections' + echo 'rbl.min 0' + echo 'rbl.draw AREA' + echo 'greylisted.label Greylisted connections' + echo 'greylisted.min 0' + echo 'greylisted.draw STACK' + echo 'accepted.label Accepted connections' + echo 'accepted.min 0' + echo 'accepted.draw STACK' + echo 'simscan_spam.label Rejected SPAM' + echo 'simscan_spam.min 0' + echo 'simscan_spam.draw STACK' + echo 'simscan_virus.label Rejected VIRUS' + echo 'simscan_virus.min 0' + echo 'simscan_virus.draw STACK' + echo 'total.label Total connections' + echo 'total.min 0' + echo 'total.draw LINE1' exit 0 fi -rbl=`cat /var/log/qmail/smtpd/@* /var/log/qmail/smtpd/current | grep -c rblsmtp` -accepted=`cat /var/log/qmail/smtpd/@* /var/log/qmail/smtpd/current | grep -c 'tcpserver: ok'` +rbl=`cat $LOGPATH/@* $LOGPATH/current | grep -c rblsmtp` +accepted=`cat $LOGPATH/@* $LOGPATH/current | grep -c 'tcpserver: ok'` +greylisted=`cat $LOGPATH/@* $LOGPATH/current | grep -ce "jgreylist\[[[:digit:]]\+\]: .\+\: GREY"` +simscan_spam=`cat $LOGPATH/@* $LOGPATH/current | grep -ce "simscan:\[[[:digit:]]\+\]:SPAM"` +simscan_virus=`cat $LOGPATH/@* $LOGPATH/current | grep -ce "simscan:\[[[:digit:]]\+\]:VIRUS"` echo -n "rbl.value " && ( echo $rbl || echo U ) echo -n "accepted.value " && ( echo $accepted || echo U ) -echo "total.value $[$rbl + $accepted]" +echo -n "greylisted.value " && ( echo $greylisted || echo U ) +echo -n "simscan_spam.value " && ( echo $simscan_spam || echo U ) +echo -n "simscan_virus.value " && ( echo $simscan_virus || echo U ) +echo "total.value $(expr $rbl + $accepted + $greylisted)" diff --git a/plugins/mysql/mysql_size_ondisk b/plugins/mysql/mysql_size_ondisk new file mode 100755 index 00000000..c18b9a38 --- /dev/null +++ b/plugins/mysql/mysql_size_ondisk @@ -0,0 +1,80 @@ +#!/bin/bash +# +INFO=\ +"mysql_size_ondisk - Munin plugin that reports the size of the files and + directories in /var/lib/mysql, biggest to smallest. + To correctly count InnoDB tables you should have innodb_file_per_table enabled + in MySQL (good practise anyway), otherwise all InnoDB tables will be counted into + ibdataX. + This plugin must be run as the user mysql, to do that append the following + to your /etc/munin/plugins/plugin-conf.d/munin-node: + + [mysql_size_ondisk] + user mysql + + This plugin gives you similar information as mysql_size_all. A difference + is that mysql_size_ondisk is much faster (0.4 seconds vs 14 seconds, on a server + with 170 databases, 26 GB total). Also note that mysql_size_all gives you the net + data size, mysql_size_ondisk gives you the gross storage space used, which may be + much more than your actual data." +# +# License: GPLv2 or later +# +# v1.0, 27.01.2012 Jakob Unterwurzacher + +#%# family=auto +#%# capabilities=autoconf + +set -eu + +DIR=/var/lib/mysql + +function clean { + # First character must not be a number + a=${1/#[0-9]/_} + # Other characters must be alphanumeric + b=${a//[^a-zA-Z0-9]/_} + echo $b +} + +if [ "${1:-}" = "autoconf" ] +then + if du -sb $DIR &> /dev/null + then + echo "yes" + exit 0 + else + echo "no" + exit 1 + fi +elif [ "${1:-}" = "config" ] +then + echo "graph_title MySQL on-disk database size" + echo "graph_category mysql" + # graph_info cannot have newlines - replace by
which will be rendered to newlines in + # the web interface. + echo "graph_info ${INFO//$'\n'/
}" + echo "graph_args --base 1024 --lower-limit 0" + echo "graph_vlabel Bytes" + cd $DIR + du -sb * | sort -nr | while read s i + do + i=`clean $i` + echo "$i.label $i" + echo "$i.type GAUGE" + echo "$i.draw AREASTACK" + done + exit 0 +elif [ "${1:-}" = "" ] +then + cd $DIR + du -sb * | sort -nr | while read s i + do + i=`clean $i` + echo "$i.value $s" + done + exit 0 +else + echo "Unknown argument" + exit 1 +fi diff --git a/plugins/network/if1sec_ b/plugins/network/if1sec_ new file mode 100755 index 00000000..6e5ad6d4 --- /dev/null +++ b/plugins/network/if1sec_ @@ -0,0 +1,59 @@ +#! /bin/sh + +pluginfull="$0" # full name of plugin +plugin="${0##*/}" # name of plugin +pidfile="$MUNIN_PLUGSTATE/munin.$plugin.pid" +cache="$MUNIN_PLUGSTATE/munin.$plugin.value" + +IFACE="${0##*/if1sec_}" # interface + +if [ ! -r "/sys/class/net/$IFACE/statistics/tx_bytes" ] +then + echo "# Unknown Interface : $IFACE" + exit 1 +fi + +if [ "$1" = "acquire" ] +then + ( + while sleep 1 + do + echo $( + date +%s + cat /sys/class/net/$IFACE/statistics/tx_bytes + cat /sys/class/net/$IFACE/statistics/rx_bytes + ) + done | awk "{ + print \"${IFACE}_tx.value \" \$1 \":\" \$2; + print \"${IFACE}_rx.value \" \$1 \":\" \$3; + system(\"\"); + }" >> $cache + ) & + echo $! > $pidfile + exit 0 +fi + + +if [ "$1" = "config" ] +then + cat < ${cache} + +exit 0 diff --git a/plugins/network/netatalk b/plugins/network/netatalk old mode 100644 new mode 100755 diff --git a/plugins/network/ping/ping b/plugins/network/ping/ping index 010d39ad..858fb6d3 100755 --- a/plugins/network/ping/ping +++ b/plugins/network/ping/ping @@ -164,6 +164,12 @@ for (my $host_i = 0; $host_i < @host_addrs; ++$host_i) { 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"; + my ( $warning, $critical ) = + get_thresholds($h_norm_name); + printf "%s.warning %s\n", $h_norm_name, $warning + if $warning; + printf "%s.critical %s\n", $h_norm_name, $critical + if $critical; } else { my $pid = $fork ? fork() : -1; if ($pid <= 0) { diff --git a/plugins/network/qos_ b/plugins/network/qos_ index 6382cfe1..3b9bc6fa 100755 --- a/plugins/network/qos_ +++ b/plugins/network/qos_ @@ -16,6 +16,8 @@ # tc - Override default program # ignore_queue - Queue with handle not be plotted # label_name - Queue with handle as defined label +# update_rate - Custom update_rate, to be used with async +# graph_data_size - Custom graph_data_size, to be used with async & custom update_rate # # Magic markers: #%# family=manual @@ -100,6 +102,8 @@ if ( exists $ARGV[0] and $ARGV[0] eq 'config' ) { print "graph_vlabel bits per \${graph_period}\n"; print "graph_category network\n"; print "graph_info This graph shows the QoS queue of the $IFACE network interface.\n"; + print "update_rate $ENV{update_rate}\n" if $ENV{update_rate}; + print "graph_data_size $ENV{graph_data_size}\n" if $ENV{graph_data_size}; print "graph_order "; foreach my $key (sort by_handle keys %queues) { $haschild = 0; diff --git a/plugins/network/traffic b/plugins/network/traffic index 5df88dc6..a3f8f09e 100755 --- a/plugins/network/traffic +++ b/plugins/network/traffic @@ -22,6 +22,8 @@ If trouble reading files, use: =item 2012.09.20: Initial version by Arturo Borrero Gonzalez +=item 2013.01.12: Added percentage graphing by Michiel Holtkamp + =back =head1 LICENSE @@ -39,6 +41,7 @@ GPLv2 if [ "$1" == "config" ] then cat <<'EOF' +multigraph traffic graph_title Throughput by IP protocol graph_vlabel bits per ${graph_period} graph_category network @@ -55,6 +58,32 @@ total.label Total bps total.min 0 total.type DERIVE total.draw LINE1 +EOF + + # Adapted from http://munin-monitoring.org/wiki/PercentGraphHowto + cat <<'EOF' +multigraph traffic_percent +graph_scale no +graph_title Throughput of IP protocols by percentage +graph_vlabel Percentage +graph_order IPv4=traffic.IPv4 IPv6=traffic.IPv6 total=traffic.total IPv4_percent=traffic.total IPv6_percent=traffic.total total_percent=traffic.total +graph_category network +graph_args --upper-limit 100 -l 0 -r +IPv4.label no +IPv6.label no +total.label no +total_percent.label no +IPv4.graph no +IPv6.graph no +total.graph no +total_percent.graph no +total_percent.cdef total,0.0000001,+ +IPv4_percent.label IPv4 +IPv4_percent.cdef IPv4,total_percent,/,100,* +IPv4_percent.draw AREASTACK +IPv6_percent.label IPv6 +IPv6_percent.cdef IPv6,total_percent,/,100,* +IPv6_percent.draw AREASTACK EOF exit 0 fi diff --git a/plugins/network/traffic_ipt b/plugins/network/traffic_ipt new file mode 100755 index 00000000..d03b45b2 --- /dev/null +++ b/plugins/network/traffic_ipt @@ -0,0 +1,128 @@ +#!/bin/bash +# -*- bash -*- + +: << =cut + +=head1 NAME + +traffic - Plugin to monitor the traffic (throughput) by IP protocols. + +=head1 CONFIGURATION + +To make this plugin work, you need to add rules to your firewall. +They are empty rules, we only use them to count traffic, not do anything +with them. To make this plugin work correctly, these rules have to +in the beginning of the chain(s), or else traffic that matches rules +above will not be counted (you can use this to your advantage of course). + +The rules can be added with: +iptables -I INPUT +iptables -I OUTPUT +ip6tables -I INPUT +ip6tables -I OUTPUT + +If trouble reading output, use: + + [traffic_ipt] + user root + +=head1 AUTHORS + +=over + +=item 2012.09.20: Initial version by Arturo Borrero Gonzalez + +=item 2013.01.12: Added percentage graphing by Michiel Holtkamp + +=item 2013.02.03: Converted to use iptables/ip6tables by Michiel Holtkamp + +=back + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + + +if [ "$1" == "config" ] +then + cat <<'EOF' +multigraph traffic_ipt +graph_title Throughput by IP protocol +graph_vlabel bits per ${graph_period} +graph_category network +graph_args --base 1000 --upper-limit 100 -l 0 +IPv4.label IPv4 bps +IPv4.min 0 +IPv4.type DERIVE +IPv4.draw AREA +IPv6.label IPv6 bps +IPv6.min 0 +IPv6.type DERIVE +IPv6.draw STACK +total.label Total bps +total.min 0 +total.type DERIVE +total.draw LINE1 +EOF + + # Adapted from http://munin-monitoring.org/wiki/PercentGraphHowto + cat <<'EOF' +multigraph traffic_ipt_percent +graph_scale no +graph_title Throughput of IP protocols by percentage +graph_vlabel Percentage +graph_order IPv4=traffic_ipt.IPv4 IPv6=traffic_ipt.IPv6 total=traffic_ipt.total IPv4_percent=traffic_ipt.total IPv6_percent=traffic_ipt.total total_percent=traffic_ipt.total +graph_category network +graph_args --upper-limit 100 -l 0 -r +IPv4.label no +IPv6.label no +total.label no +total_percent.label no +IPv4.graph no +IPv6.graph no +total.graph no +total_percent.graph no +total_percent.cdef total,0.0000001,+ +IPv4_percent.label IPv4 +IPv4_percent.cdef IPv4,total_percent,/,100,* +IPv4_percent.draw AREASTACK +IPv6_percent.label IPv6 +IPv6_percent.cdef IPv6,total_percent,/,100,* +IPv6_percent.draw AREASTACK +EOF + exit 0 +fi + + +ipv4=0 +ipv6=0 + +IPv4_bytes=$(iptables -L -n -v -x | egrep '^\W+[0-9]+\W+[0-9]+\W+all\W+--\W+\*\W+\*\W+0.0.0.0/0\W+0.0.0.0/0\W+$' | while read pkts bytes rest; do echo $bytes; done) +if [ -z "$IPv4_bytes" ]; +then + echo "W: Unable to read rule from iptables, please add rules" >&2 +else + ipv4=$(echo $IPv4_bytes | sed -e 's/ / + /' | bc -l) +fi + +IPv6_bytes=$(ip6tables -L -n -v -x | egrep '^\W+[0-9]+\W+[0-9]+\W+all\W+\*\W+\*\W+::/0\W+::/0\W+$' | while read pkts bytes rest; do echo $bytes; done) +if [ -z "$IPv6_bytes" ]; +then + echo "W: Unable to read rule from ip6tables, please add rules" >&2 +else + ipv6=$(echo $IPv6_bytes | sed -e 's/ / + /' | bc -l) +fi + +echo "IPv4.value $ipv4" +echo "IPv6.value $ipv6" +echo "total.value $( echo $ipv4 + $ipv6 | bc )" + +exit 0 + diff --git a/plugins/other/bitcoind_ b/plugins/other/bitcoind_ index bf75a589..a62cb7a2 100755 --- a/plugins/other/bitcoind_ +++ b/plugins/other/bitcoind_ @@ -56,6 +56,7 @@ def main(): 'transactions': ("Transactions", "Transactions", ('confirmed', 'waiting')), 'block_age': ("Last Block Age", "Seconds"), + 'difficulty': ("Difficulty", ""), } labels = request_labels[request_var] if len(labels) < 3: diff --git a/plugins/qpid/qpid_bytedepth b/plugins/qpid/qpid_bytedepth new file mode 100755 index 00000000..43959fe8 --- /dev/null +++ b/plugins/qpid/qpid_bytedepth @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# +# Plugin to monitor Apache Qpid +# - graphs the number of outstanding bytes on queue(s) specified in config +# +# Parameters understood: +# +# queues (required) - space separated list of queues to display (regex allowed) +# +# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk) +# +# Licence: GPLv2 +# + +import re +import sys +import os +from qmf.console import Session + +if not "queues" in os.environ: + print >> sys.stderr, "Missing env.queues in config" + sys.exit(-1) + +output_queue = [] +sess = Session() +broker = sess.addBroker() +queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker") +for q in queues: + for match in os.environ["queues"].split(" "): + if re.match(match, q.name): + output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) + +if len(sys.argv) > 1 and sys.argv[1] == "config": + print "graph_category Qpid"; + print "graph_title Queue byte depth"; + print "graph_vlabel bytes" + for queue in output_queue: + print "%s.label %s" % (queue, queue) + print "%s.min 0" % queue + print "%s.type GAUGE" % queue +else: + for q in queues: + qname = re.sub('[^a-zA-Z0-9_]', '_', q.name) + if qname in output_queue: + print "%s.value %u" % (qname, q.byteDepth) + +sess.delBroker(broker) + diff --git a/plugins/qpid/qpid_discardsring b/plugins/qpid/qpid_discardsring new file mode 100755 index 00000000..62d85d1b --- /dev/null +++ b/plugins/qpid/qpid_discardsring @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# +# Plugin to monitor Apache Qpid +# - graphs the number of messages discarded from queue(s) specified in config +# +# Parameters understood: +# +# queues (required) - space separated list of queues to display (regex allowed) +# +# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk) +# +# Licence: GPLv2 +# + +import re +import sys +import os +from qmf.console import Session + +if not "queues" in os.environ: + print >> sys.stderr, "Missing env.queues in config" + sys.exit(-1) + +output_queue = [] +sess = Session() +broker = sess.addBroker() +queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker") +for q in queues: + for match in os.environ["queues"].split(" "): + if re.match(match, q.name): + output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) + +if len(sys.argv) > 1 and sys.argv[1] == "config": + print "graph_category Qpid"; + print "graph_title Ring queue discard rate"; + print "graph_vlabel messages/second"; + for queue in output_queue: + print "%s.label %s" % (queue, queue) + print "%s.min 0" % queue + print "%s.type COUNTER" % queue +else: + for q in queues: + qname = re.sub('[^a-zA-Z0-9_]', '_', q.name) + if qname in output_queue: + print "%s.value %u" % (qname, q.discardsRing) + +sess.delBroker(broker) + diff --git a/plugins/qpid/qpid_enqueuebytes b/plugins/qpid/qpid_enqueuebytes new file mode 100755 index 00000000..8afc420f --- /dev/null +++ b/plugins/qpid/qpid_enqueuebytes @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# +# Plugin to monitor Apache Qpid +# - graph ingest rate (bytes/sec) of queue(s) specified in config +# +# Parameters understood: +# +# queues (required) - space separated list of queues to display (regex allowed) +# +# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk) +# +# Licence: GPLv2 +# + +import re +import sys +import os +from qmf.console import Session + +if not "queues" in os.environ: + print >> sys.stderr, "Missing env.queues in config" + sys.exit(-1) + +output_queue = [] +sess = Session() +broker = sess.addBroker() +queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker") +for q in queues: + for match in os.environ["queues"].split(" "): + if re.match(match, q.name): + output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) + +if len(sys.argv) > 1 and sys.argv[1] == "config": + print "graph_category Qpid"; + print "graph_title Enqueue data rate"; + print "graph_vlabel bytes/second" + for queue in output_queue: + print "%s.label %s" % (queue, queue) + print "%s.min 0" % queue + print "%s.type COUNTER" % queue +else: + for q in queues: + qname = re.sub('[^a-zA-Z0-9_]', '_', q.name) + if qname in output_queue: + print "%s.value %u" % (qname, q.byteTotalEnqueues) + +sess.delBroker(broker) + diff --git a/plugins/qpid/qpid_enqueuecount b/plugins/qpid/qpid_enqueuecount new file mode 100755 index 00000000..45420d89 --- /dev/null +++ b/plugins/qpid/qpid_enqueuecount @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# +# Plugin to monitor Apache Qpid +# - graphs ingest rate (messages/sec) of queue(s) specified in config +# +# Parameters understood: +# +# queues (required) - space separated list of queues to display (regex allowed) +# +# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk) +# +# Licence: GPLv2 +# + +import re +import sys +import os +from qmf.console import Session + +if not "queues" in os.environ: + print >> sys.stderr, "Missing env.queues in config" + sys.exit(-1) + +output_queue = [] +sess = Session() +broker = sess.addBroker() +queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker") +for q in queues: + for match in os.environ["queues"].split(" "): + if re.match(match, q.name): + output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) + +if len(sys.argv) > 1 and sys.argv[1] == "config": + print "graph_category Qpid"; + print "graph_title Enqueue message rate"; + print "graph_vlabel messages/second" + for queue in output_queue: + print "%s.label %s" % (queue, queue) + print "%s.min 0" % queue + print "%s.type COUNTER" % queue +else: + for q in queues: + qname = re.sub('[^a-zA-Z0-9_]', '_', q.name) + if qname in output_queue: + print "%s.value %u" % (qname, q.msgTotalEnqueues) + +sess.delBroker(broker) + diff --git a/plugins/qpid/qpid_msgdepth b/plugins/qpid/qpid_msgdepth new file mode 100755 index 00000000..33143d00 --- /dev/null +++ b/plugins/qpid/qpid_msgdepth @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# +# Plugin to monitor Apache Qpid +# - graphs the number of outstanding messages on queue(s) specified in config +# +# Parameters understood: +# +# queues (required) - space separated list of queues to display (regex allowed) +# +# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk) +# +# Licence: GPLv2 +# + +import re +import sys +import os +from qmf.console import Session + +if not "queues" in os.environ: + print >> sys.stderr, "Missing env.queues in config" + sys.exit(-1) + +output_queue = [] +sess = Session() +broker = sess.addBroker() +queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker") +for q in queues: + for match in os.environ["queues"].split(" "): + if re.match(match, q.name): + output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) + +if len(sys.argv) > 1 and sys.argv[1] == "config": + print "graph_category Qpid"; + print "graph_title Queue message depth"; + print "graph_vlabel messages" + for queue in output_queue: + print "%s.label %s" % (queue, queue) + print "%s.min 0" % queue + print "%s.type GAUGE" % queue +else: + for q in queues: + qname = re.sub('[^a-zA-Z0-9_]', '_', q.name) + if qname in output_queue: + print "%s.value %u" % (qname, q.msgDepth) + +sess.delBroker(broker) + diff --git a/plugins/sensors/esxi__sensors b/plugins/sensors/esxi__sensors index d33b507f..87556193 100755 --- a/plugins/sensors/esxi__sensors +++ b/plugins/sensors/esxi__sensors @@ -27,6 +27,11 @@ file: env.user monitor env.pass passw0rd +To create an account just for monitoring the ESXi, add a user to the host, +make it a member of the 'root' group, and give it the 'No access' role. +That will allow the account to connect via WBEM, but not via any of the +management tools. + In the event that not all sensor types are desired, the plugin can be limited to a subset of the types available: diff --git a/plugins/sensors/freeipmi b/plugins/sensors/freeipmi index 0d34fb06..feb64189 100755 --- a/plugins/sensors/freeipmi +++ b/plugins/sensors/freeipmi @@ -17,10 +17,29 @@ When used for the local host, the plugin should be linked as non-wildcard plugin, i.e., 'freeipmi', whereas when used to monitor a foreign host it should be, e.g., 'freeipmi_192.168.0.253'. +=head1 DEPENDENCIES + +The plugin requires FreeIPMI 1.1.5 or later to fetch the information. +Limits set on thresholds are available when using FreeIPMI 1.2.0 or +later. + =head1 AUTHOR Diego Elio Pettenò . +With help and suggestions of: + +Bart ten Brinke + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + =head1 LICENSE GPLv2 @@ -42,12 +61,15 @@ my $IPMISENSORS = $ENV{'ipmisensors'} || 'ipmi-sensors'; $0 =~ /freeipmi(?:_(.+))$/; my $hostname = $1; -$IPMISENSORS .= " --quiet-cache --comma-separated-output --no-header-output --ignore-not-available-sensors --sensor-types=Temperature,Fan,Current,Voltage --output-sensor-thresholds"; +my $help_output = `$IPMISENSORS --help`; + +$IPMISENSORS .= " --output-sensor-thresholds" if $help_output =~ /--output-sensor-thresholds/; +$IPMISENSORS .= " --quiet-cache --comma-separated-output --no-header-output --ignore-not-available-sensors --sensor-types=Temperature,Fan,Current,Voltage"; $IPMISENSORS .= " --hostname=$hostname" if defined($hostname); $IPMISENSORS .= " --username=$ENV{IPMI_USERNAME}" if defined($ENV{IPMI_USERNAME}); $IPMISENSORS .= " --password=$ENV{IPMI_PASSWORD}" if defined($ENV{IPMI_PASSWORD}); -my $output=`$IPMISENSORS --output-sensor-thresholds 2>/dev/null`; +my $output=`$IPMISENSORS 2>/dev/null`; my $retval=$?; if ( defined $ARGV[0] and $ARGV[0] eq 'autoconf' ) { @@ -109,11 +131,11 @@ foreach my $line (@data) { value => $dataline[3], label => $dataline[1] ); - $sensor{lwarn} = $dataline[7] ne "N/A" ? $dataline[7] : ''; - $sensor{hwarn} = $dataline[8] ne "N/A" ? $dataline[8] : ''; + $sensor{lwarn} = (defined($dataline[7]) and $dataline[7] ne "N/A") ? $dataline[7] : ''; + $sensor{hwarn} = (defined($dataline[8]) and $dataline[8] ne "N/A") ? $dataline[8] : ''; - $sensor{lcrit} = $dataline[6] ne "N/A" ? $dataline[6] : ''; - $sensor{hcrit} = $dataline[9] ne "N/A" ? $dataline[9] : ''; + $sensor{lcrit} = (defined($dataline[6]) and $dataline[6] ne "N/A") ? $dataline[6] : ''; + $sensor{hcrit} = (defined($dataline[9]) and $dataline[9] ne "N/A") ? $dataline[9] : ''; my $type; if ( $dataline[2] eq "Temperature" ) { @@ -158,7 +180,7 @@ END } } - unless ( $ENV{MUNIN_CAP_DIRTYCONFIG} == 1 ) { + unless ( ($ENV{MUNIN_CAP_DIRTYCONFIG} || 0) == 1 ) { exit 0; } } diff --git a/plugins/sensors/hwmon b/plugins/sensors/hwmon index 5e755436..66e44801 100755 --- a/plugins/sensors/hwmon +++ b/plugins/sensors/hwmon @@ -173,7 +173,7 @@ END } } - unless ( $ENV{MUNIN_CAP_DIRTYCONFIG} == 1 ) { + unless ( ($ENV{MUNIN_CAP_DIRTYCONFIG} || 0) == 1 ) { exit 0; } } diff --git a/plugins/sickbeard/sickbeard_episodes b/plugins/sickbeard/sickbeard_episodes new file mode 100755 index 00000000..2a6eb7d5 --- /dev/null +++ b/plugins/sickbeard/sickbeard_episodes @@ -0,0 +1,69 @@ +#!/usr/bin/perl +# +# Munin plugins for Sick-Beard +# +# Copyright (C) 2012 - Blauwbek +# Copyright (C) 2012 - Thiago +# +# Sick-Beard : http://sickbeard.com/ +# +# 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see . +# +# Requires: JSON::Any +# LWP::UserAgent +# +# Configuration example +# [sickbeard*] +# env.host http://host:port/ +# env.api apikey +# + +use strict; +use JSON::Any; +use LWP::UserAgent; + +#defines +my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "http://localhost:8081/"; +my $API = exists $ENV{'api'} ? $ENV{'api'} : ""; +my $URL = $HOST."/api/".$API."/?cmd=shows.stats"; +my $sURL = sprintf $URL; + +#config output +if(defined $ARGV[0] && $ARGV[0] eq 'config') +{ + print <new; +my $req = $get->get($sURL); +my $json = JSON::Any->jsonToObj($req->content()); + +if ($json->{result} eq 'success') { + print "down.value $json->{data}->{ep_downloaded}\n"; + print "total.value $json->{data}->{ep_total}\n"; + exit 0; +} else { + print "$json->{message}\n"; + exit 1; +} + diff --git a/plugins/sickbeard/sickbeard_shows b/plugins/sickbeard/sickbeard_shows new file mode 100755 index 00000000..a81c6bd8 --- /dev/null +++ b/plugins/sickbeard/sickbeard_shows @@ -0,0 +1,69 @@ +#!/usr/bin/perl +# +# Munin plugins for Sick-Beard +# +# Copyright (C) 2012 - Blauwbek +# Copyright (C) 2012 - Thiago +# +# Sick-Beard : http://sickbeard.com/ +# +# 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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, see . +# +# Requires: JSON::Any +# LWP::UserAgent +# +# Configuration example +# [sickbeard*] +# env.host http://host:port/ +# env.api apikey +# + +use strict; +use JSON::Any; +use LWP::UserAgent; + +#defines +my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "http://localhost:8081/"; +my $API = exists $ENV{'api'} ? $ENV{'api'} : ""; +my $URL = $HOST."/api/".$API."/?cmd=shows.stats"; +my $sURL = sprintf $URL; + +#config output +if(defined $ARGV[0] && $ARGV[0] eq 'config') +{ + print <new; +my $req = $get->get($sURL); +my $json = JSON::Any->jsonToObj($req->content()); + +if ($json->{result} eq 'success') { + print "active.value $json->{data}->{shows_active}\n"; + print "total.value $json->{data}->{shows_total}\n"; + exit 0; +} else { + print "$json->{message}\n"; + exit 1; +} + diff --git a/plugins/system/cpu_by_process b/plugins/system/cpu_by_process new file mode 100755 index 00000000..504ff309 --- /dev/null +++ b/plugins/system/cpu_by_process @@ -0,0 +1,106 @@ +#!/usr/bin/perl +# +# Copyright 2012 Chris Wilson +# Copyright 2006 Holger Levsen +# +# This plugin monitors ALL processes on a system. No exceptions. It can +# produce very big graphs! But if you want to know where your CPU time +# is going without knowing what to monitor in advance, this can help; +# or in addition to one of the more specific CPU plugins to monitor +# just Apache or MySQL, for example. +# +# It's not obvious what the graph heights actually mean, even to me. +# Each counter is a DERIVE (difference since the last counter reading) +# of the CPU time usage (in seconds) accounted to each process, summed +# by the process name, so all Apache and all MySQL processes are grouped +# together. Processes with no CPU usage at all are ignored. Processes +# that die may not appear on the graph, and anyway their last chunk of +# CPU usage before they died is lost. You could modify this plugin to +# read SAR/psacct records if you care about that. +# +# 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. + +#scriptname=`basename $0` +#vsname=`echo $scriptname | perl -ne '/^vserver_proc_VM_(.*)/ and print $1'` + +#if [ "$1" = "suggest" ]; then +# ls -1 /etc/vservers +# exit 0 +#elif [ -z "$vsname" ]; then +# echo "Must be used with a vserver name; try '$0 suggest'" >&2 +# exit 2 +#fi + +use strict; +use warnings; + +my $cmd = "ps -eo time,comm h"; +open PS, "$cmd|" or die "Failed to run ps command: $cmd: $!"; + +# my $header_line = ; +my %total_cpu_by_process; + +while () +{ + my @fields = split; + my $cputime = $fields[0]; + my $process = $fields[1]; + + # remove any / and everything after it from the process name, + # e.g. kworker/0:2 -> kworker + $process =~ s|/.*||; + + # remove any . at the end of the name (why does this appear?) + # $process =~ s|\.$||; + + # change any symbol that's not allowed in a munin variable name to _ + $process =~ tr|a-zA-Z0-9|_|c; + + my @times = split /:/, $cputime; + $cputime = (($times[0] * 60) + $times[1]) * 60 + $times[2]; + $total_cpu_by_process{$process} += $cputime; +} + +foreach my $process (keys %total_cpu_by_process) +{ + # remove all processes with 0 cpu time + if (not $total_cpu_by_process{$process}) + { + delete $total_cpu_by_process{$process}; + } +} + +close(PS); + +if (@ARGV and $ARGV[1] eq "config") +{ + print < $pidfile - mpstat -P ALL $interval | - awk -v cpus=$cpus '$2>=0&&$2<10 {print $2, systime(), (100-$11)/cpus}' >> $cache + LANG=C mpstat -P ALL $interval | + awk -v cpus=$cpus '$2>=0&&$2<99 {print $2, systime(), (100-$11)/cpus; system("");}' >> $cache rm -f $pidfile $cache } diff --git a/plugins/system/pagefaults_by_process b/plugins/system/pagefaults_by_process new file mode 100755 index 00000000..00a6027f --- /dev/null +++ b/plugins/system/pagefaults_by_process @@ -0,0 +1,105 @@ +#!/usr/bin/perl +# +# Copyright 2012 Chris Wilson +# Copyright 2006 Holger Levsen +# +# This plugin monitors ALL processes on a system. No exceptions. It can +# produce very big graphs! But if you want to know which processes are +# killing your system by page faulting, without knowing what to monitor +# in advance, this can help; or in addition to one of the more specific +# plugins to monitor just Apache or MySQL, for example. +# +# Each counter is a DERIVE (difference since the last counter reading) +# of the number of major page faults, usually 4k each, read in by a +# process. Memory mapped files probably contribute to this. The process +# cannot continue until the page fault is served, so this is a +# high-priority read that usually indicates memory starvation. +# Processes with no page faults at all are ignored. Processes +# that die may not appear on the graph, and anyway their last chunk of +# CPU usage before they died is lost. You could modify this plugin to +# read SAR/psacct records if you care about that. +# +# 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. + +#scriptname=`basename $0` +#vsname=`echo $scriptname | perl -ne '/^vserver_proc_VM_(.*)/ and print $1'` + +#if [ "$1" = "suggest" ]; then +# ls -1 /etc/vservers +# exit 0 +#elif [ -z "$vsname" ]; then +# echo "Must be used with a vserver name; try '$0 suggest'" >&2 +# exit 2 +#fi + +use strict; +use warnings; + +my $cmd = "ps -eo maj_flt,comm h"; +open PS, "$cmd|" or die "Failed to run ps command: $cmd: $!"; + +# my $header_line = ; +my %total_by_process; + +while () +{ + my @fields = split; + my $value = $fields[0]; + my $process = $fields[1]; + + # remove any / and everything after it from the process name, + # e.g. kworker/0:2 -> kworker + $process =~ s|/.*||; + + # remove any . at the end of the name (why does this appear?) + # $process =~ s|\.$||; + + # change any symbol that's not allowed in a munin variable name to _ + $process =~ tr|a-zA-Z0-9|_|c; + + $total_by_process{$process} += $value; +} + +foreach my $process (keys %total_by_process) +{ + # remove all processes with 0 faults + if (not $total_by_process{$process}) + { + delete $total_by_process{$process}; + } +} + +close(PS); + +if (@ARGV and $ARGV[1] eq "config") +{ + print <; +my %total_by_process; + +while () +{ + my @fields = split; + my $value = $fields[0]; + my $process = $fields[1]; + + # remove any / and everything after it from the process name, + # e.g. kworker/0:2 -> kworker + $process =~ s|/.*||; + + # remove any . at the end of the name (why does this appear?) + # $process =~ s|\.$||; + + # change any symbol that's not allowed in a munin variable name to _ + $process =~ tr|a-zA-Z0-9|_|c; + + $total_by_process{$process} += $value; +} + +foreach my $process (keys %total_by_process) +{ + # remove all processes with 0 faults + if (not $total_by_process{$process}) + { + delete $total_by_process{$process}; + } +} + +close(PS); + +if (@ARGV and $ARGV[1] == "config") +{ + print <" + +Modules: cput, cpup, mem, disk, net Then restart munin-node and you're done. @@ -34,10 +37,14 @@ NOTE: xentop always reports 0 for dom0's disk IOs and network traffic, but both graphs show its entry all the same, so each domain can keep its own color along the different graphs. -=head2 CPU usage +=head2 CPU time This graph shows a percentage of the CPU time used by each domain. +=head2 CPU usage + +This graph shows a percentage of the CPU percentage used by each domain. + =head2 Memory usage This graph shows the amount of memory (in bytes) used by each domain. @@ -66,6 +73,7 @@ Michael Renner for the C plugin which I borrowed some code from. =head1 AUTHOR Raphael HALIMI +Modified by Krisztian IVANCSO =head1 LICENSE @@ -76,6 +84,8 @@ GPLv2 use strict; use Munin::Plugin; +my $XEN_SKIP = $ENV{xenskip} || ""; + # autoconf - quite simple if ($ARGV[0] eq "autoconf") { if (`which xentop`) { @@ -101,7 +111,7 @@ if ($ARGV[0] eq "autoconf") { sub trim_label { my ($type, $label) = @_; my $data_characters; - my ($graph_width, $graph_border_width, $padding_characters, $pixels_per_character) = (400,97,10,6); + my ($graph_width, $graph_border_width, $padding_characters, $pixels_per_character) = (497,3,5,6); if ($type eq 'pos') {$data_characters = 32;} elsif ($type eq 'neg') {$data_characters = 64;} else {return $label;} my $available_characters = abs(($graph_width+$graph_border_width)/$pixels_per_character)-$padding_characters-$data_characters; @@ -117,9 +127,9 @@ sub trim_label { } # Global variables -my (%domains,$domain,$munindomain,$cpusecs,$memk,$nettxk,$netrxk,$vbdrd,$vbdwr); +my (%domains,$domain,@domainlist,$munindomain,$cpusecs,$cpupercent,$memk,$nettxk,$netrxk,$vbdrd,$vbdwr); -open (XENTOP,"xentop -b -f -i1 |") or die "Could not execute xentop, $!"; +open (XENTOP,"xentop -b -f -i2 |") or die "Could not execute xentop, $!"; # Now we build a hash of hashes to store information while () { @@ -129,7 +139,7 @@ while () { next if /^NAME/; # We define only what we need - ($domain,undef,$cpusecs,undef,$memk,undef,undef,undef,undef,undef,$nettxk,$netrxk,undef,undef,$vbdrd,$vbdwr,undef,undef,undef) = split(/\s+/); + ($domain,undef,$cpusecs,$cpupercent,$memk,undef,undef,undef,undef,undef,$nettxk,$netrxk,undef,undef,$vbdrd,$vbdwr,undef,undef,undef) = split(/\s+/); # We have to store the sanitised domain name in a separate variable $domains{$domain}{'munindomain'} = clean_fieldname($domain); @@ -137,6 +147,7 @@ while () { # We need the remaining data only for a normal run if ($ARGV[0] eq "") { $domains{$domain}{'cpusecs'} = $cpusecs; + $domains{$domain}{'cpupercent'} = $cpupercent; $domains{$domain}{'mem'} = $memk; $domains{$domain}{'nettx'} = $nettxk; $domains{$domain}{'netrx'} = $netrxk; @@ -145,71 +156,94 @@ while () { } } +@domainlist = sort(keys(%domains)); # # config - quite simple, too # if ($ARGV[0] eq "config") { - print "multigraph xen_cpu_time\n"; - print "graph_title Xen domains CPU time\n"; - print "graph_args --base 1000 -l 0\n"; - print "graph_vlabel %\n"; - print "graph_scale no\n"; - print "graph_category xen\n"; - print "graph_info This graph shows CPU time for each Xen domain.\n"; - for $domain (sort(keys(%domains))) { - print "$domains{$domain}{'munindomain'}_cpu_time.label ".trim_label('pos',$domain)."\n"; - print "$domains{$domain}{'munindomain'}_cpu_time.type DERIVE\n"; - print "$domains{$domain}{'munindomain'}_cpu_time.cdef $domains{$domain}{'munindomain'}_cpu_time,100,*\n"; - print "$domains{$domain}{'munindomain'}_cpu_time.min 0\n"; - print "$domains{$domain}{'munindomain'}_cpu_time.draw AREASTACK\n"; + if ($XEN_SKIP !~ /cput/) { + print "multigraph xen_cpu_time\n"; + print "graph_title Xen domains CPU time\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel cpu seconds\n"; + print "graph_scale no\n"; + print "graph_category xen\n"; + print "graph_info This graph shows CPU time for each Xen domain.\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_cpu_time.label ".trim_label('pos',$domain)."\n"; + print "$domains{$domain}{'munindomain'}_cpu_time.type DERIVE\n"; + print "$domains{$domain}{'munindomain'}_cpu_time.cdef $domains{$domain}{'munindomain'}_cpu_time,100,*\n"; + print "$domains{$domain}{'munindomain'}_cpu_time.min 0\n"; + print "$domains{$domain}{'munindomain'}_cpu_time.draw AREASTACK\n"; + } } - print "\nmultigraph xen_mem\n"; - print "graph_title Xen domains memory usage\n"; - print "graph_args --base 1024 -l 0\n"; - print "graph_vlabel bytes\n"; - print "graph_category xen\n"; - print "graph_info This graph shows memory usage for each Xen domain.\n"; - for $domain (sort(keys(%domains))) { - print "$domains{$domain}{'munindomain'}_mem.label ".trim_label('pos',$domain)."\n"; - print "$domains{$domain}{'munindomain'}_mem.cdef $domains{$domain}{'munindomain'}_mem,1024,*\n"; - print "$domains{$domain}{'munindomain'}_mem.draw AREASTACK\n"; + if ($XEN_SKIP !~ /cpup/) { + print "\nmultigraph xen_cpu\n"; + print "graph_title Xen domains CPU utilization\n"; + print "graph_args --base 1000 -l 0 --upper-limit 100\n"; + print "graph_vlabel %\n"; + print "graph_scale no\n"; + print "graph_category xen\n"; + print "graph_info This graph shows CPU utilization for each Xen domain.\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_cpu.label ".trim_label('pos',$domain)."\n"; + print "$domains{$domain}{'munindomain'}_cpu.draw AREASTACK\n" + } } - print "\nmultigraph xen_net\n"; - print "graph_title Xen domains network traffic\n"; - print "graph_args --base 1000\n"; - print "graph_vlabel bits per \${graph_period} in (-) / out (+)\n"; - print "graph_category xen\n"; - print "graph_info This graph shows network traffic for each Xen domain.\n"; - for $domain (sort(keys(%domains))) { - print "$domains{$domain}{'munindomain'}_netrx.label none\n"; - print "$domains{$domain}{'munindomain'}_netrx.cdef $domains{$domain}{'munindomain'}_netrx,8192,*\n"; - print "$domains{$domain}{'munindomain'}_netrx.type COUNTER\n"; - print "$domains{$domain}{'munindomain'}_netrx.graph no\n"; - print "$domains{$domain}{'munindomain'}_nettx.label ".trim_label('neg',$domain)."\n"; - print "$domains{$domain}{'munindomain'}_nettx.cdef $domains{$domain}{'munindomain'}_nettx,8192,*\n"; - print "$domains{$domain}{'munindomain'}_nettx.type COUNTER\n"; - print "$domains{$domain}{'munindomain'}_nettx.draw AREASTACK\n"; - print "$domains{$domain}{'munindomain'}_nettx.negative $domains{$domain}{'munindomain'}_netrx\n"; + if ($XEN_SKIP !~ /mem/) { + print "\nmultigraph xen_mem\n"; + print "graph_title Xen domains memory usage\n"; + print "graph_args --base 1024 -l 0\n"; + print "graph_vlabel bytes\n"; + print "graph_category xen\n"; + print "graph_info This graph shows memory usage for each Xen domain.\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_mem.label ".trim_label('pos',$domain)."\n"; + print "$domains{$domain}{'munindomain'}_mem.cdef $domains{$domain}{'munindomain'}_mem,1024,*\n"; + print "$domains{$domain}{'munindomain'}_mem.draw AREASTACK\n"; + } } - print "\nmultigraph xen_disk\n"; - print "graph_title Xen domains disk IOs\n"; - print "graph_args --base 1000\n"; - print "graph_vlabel IOs per \${graph_period} read (-) / write (+)\n"; - print "graph_category xen\n"; - print "graph_info This graph shows disk IOs for each Xen domain.\n"; - for $domain (sort(keys(%domains))) { - print "$domains{$domain}{'munindomain'}_vbdrd.label none\n"; - print "$domains{$domain}{'munindomain'}_vbdrd.type COUNTER\n"; - print "$domains{$domain}{'munindomain'}_vbdrd.graph no\n"; - print "$domains{$domain}{'munindomain'}_vbdwr.label ".trim_label('neg',$domain)."\n"; - print "$domains{$domain}{'munindomain'}_vbdwr.type COUNTER\n"; - print "$domains{$domain}{'munindomain'}_vbdwr.draw AREASTACK\n"; - print "$domains{$domain}{'munindomain'}_vbdwr.negative $domains{$domain}{'munindomain'}_vbdrd\n"; + if ($XEN_SKIP !~ /net/) { + print "\nmultigraph xen_net\n"; + print "graph_title Xen domains network traffic\n"; + print "graph_args --base 1000\n"; + print "graph_vlabel bits per \${graph_period} in (-) / out (+)\n"; + print "graph_category xen\n"; + print "graph_info This graph shows network traffic for each Xen domain.\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_netrx.label none\n"; + print "$domains{$domain}{'munindomain'}_netrx.cdef $domains{$domain}{'munindomain'}_netrx,8192,*\n"; + print "$domains{$domain}{'munindomain'}_netrx.type COUNTER\n"; + print "$domains{$domain}{'munindomain'}_netrx.graph no\n"; + print "$domains{$domain}{'munindomain'}_nettx.label ".trim_label('neg',$domain)."\n"; + print "$domains{$domain}{'munindomain'}_nettx.cdef $domains{$domain}{'munindomain'}_nettx,8192,*\n"; + print "$domains{$domain}{'munindomain'}_nettx.type COUNTER\n"; + print "$domains{$domain}{'munindomain'}_nettx.draw AREASTACK\n"; + print "$domains{$domain}{'munindomain'}_nettx.negative $domains{$domain}{'munindomain'}_netrx\n"; + } + } + + if ($XEN_SKIP !~ /disk/) { + print "\nmultigraph xen_disk\n"; + print "graph_title Xen domains disk IOs\n"; + print "graph_args --base 1000\n"; + print "graph_vlabel IOs per \${graph_period} read (-) / write (+)\n"; + print "graph_category xen\n"; + print "graph_info This graph shows disk IOs for each Xen domain.\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_vbdrd.label none\n"; + print "$domains{$domain}{'munindomain'}_vbdrd.type COUNTER\n"; + print "$domains{$domain}{'munindomain'}_vbdrd.graph no\n"; + print "$domains{$domain}{'munindomain'}_vbdwr.label ".trim_label('neg',$domain)."\n"; + print "$domains{$domain}{'munindomain'}_vbdwr.type COUNTER\n"; + print "$domains{$domain}{'munindomain'}_vbdwr.draw AREASTACK\n"; + print "$domains{$domain}{'munindomain'}_vbdwr.negative $domains{$domain}{'munindomain'}_vbdrd\n"; + } } exit 0; @@ -220,24 +254,40 @@ if ($ARGV[0] eq "config") { # Normal run # -print "multigraph xen_cpu_time\n"; -for $domain (sort(keys(%domains))) { - print "$domains{$domain}{'munindomain'}_cpu_time.value $domains{$domain}{'cpusecs'}\n"; +if ($XEN_SKIP !~ /cput/) { + print "multigraph xen_cpu_time\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_cpu_time.value $domains{$domain}{'cpusecs'}\n"; + } } -print "\nmultigraph xen_mem\n"; -for $domain (sort(keys(%domains))) { - print "$domains{$domain}{'munindomain'}_mem.value $domains{$domain}{'mem'}\n"; +if ($XEN_SKIP !~ /cpup/) { + print "\nmultigraph xen_cpu\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_cpu.value $domains{$domain}{'cpupercent'}\n"; + } } -print "\nmultigraph xen_net\n"; -for $domain (sort(keys(%domains))) { - print "$domains{$domain}{'munindomain'}_nettx.value $domains{$domain}{'nettx'}\n"; - print "$domains{$domain}{'munindomain'}_netrx.value $domains{$domain}{'netrx'}\n"; +if ($XEN_SKIP !~ /mem/) { + print "\nmultigraph xen_mem\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_mem.value $domains{$domain}{'mem'}\n"; + } } -print "\nmultigraph xen_disk\n"; -for $domain (sort(keys(%domains))) { - print "$domains{$domain}{'munindomain'}_vbdrd.value $domains{$domain}{'vbdrd'}\n"; - print "$domains{$domain}{'munindomain'}_vbdwr.value $domains{$domain}{'vbdwr'}\n"; +if ($XEN_SKIP !~ /net/) { + print "\nmultigraph xen_net\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_nettx.value $domains{$domain}{'nettx'}\n"; + print "$domains{$domain}{'munindomain'}_netrx.value $domains{$domain}{'netrx'}\n"; + } } + +if ($XEN_SKIP !~ /disk/) { + print "\nmultigraph xen_disk\n"; + for $domain (@domainlist) { + print "$domains{$domain}{'munindomain'}_vbdrd.value $domains{$domain}{'vbdrd'}\n"; + print "$domains{$domain}{'munindomain'}_vbdwr.value $domains{$domain}{'vbdwr'}\n"; + } +} + diff --git a/plugins/wowza/wowza-media-server b/plugins/wowza/wowza-media-server index 81fee466..d732687d 100755 --- a/plugins/wowza/wowza-media-server +++ b/plugins/wowza/wowza-media-server @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/python """ Plugin to monitor Wowza streaming servers. @@ -83,14 +83,14 @@ tree.parse(f) f.close() vhosts = [] -for vh in tree.iter("VHost"): +for vh in tree.findall("VHost"): if vh.find("Name").text not in vhost_exclude: applications = [] - for app in vh.iter("Application"): + for app in vh.findall("Application"): if app.find("Name").text not in app_exclude: if app.find("Status").text == "loaded": clients = [] - for client in app.iter("Client"): + for client in app.findall("Client"): if client.find("IpAddress").text not in client_exclude: clients.append({"ClientId": client.find("ClientId").text, "FlashVersion": client.find("FlashVersion").text, @@ -289,7 +289,7 @@ elif plugin_name == "wowza_vhost_uptime": elif plugin_name == "wowza_app_listeners": for vh in vhosts: for app in vh["Applications"]: - print (vh["Name"].strip("/").replace(".","_").replace("-","_"),"_",app["Name"].strip("/").replace(".","_").replace("-","_"),".value ",app["TimeRunning"],sep='') + print (vh["Name"].strip("/").replace(".","_").replace("-","_"),"_",app["Name"].strip("/").replace(".","_").replace("-","_"),".value ",app["ConnectionsCurrent"],sep='') elif plugin_name == "wowza_app_duration": alldurations = {} diff --git a/plugins/xastir/xastir b/plugins/xastir/xastir new file mode 100755 index 00000000..c8944d6b --- /dev/null +++ b/plugins/xastir/xastir @@ -0,0 +1,117 @@ +#!/bin/bash + +## Copyright (C) 2012 Robert Kilian +## +## This file is part of the Xastir plugin for Munin. +## +## Xastir-Munin 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; +## either version 3 of the License, or (at your option) any +## later version. +## +## Xastir-Munin 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 Xastir-Munin; see the file COPYING. If not, +## see . +## +## Version 0.1 -- 07.26.12 +## + +## Be sure to correctly edit the STATION_CALL, XASTIRDIR, and LOGDIR variables +## +## STATION_CALL: The callsign used by Xastir (include suffix if one is in use) +## XASTIRDIR: The directory where Xastir's data, config, etc files are found. Typically ~/.xastir +## LOGDIR: Logs are typically stored in ~/.xastir/logs. Ensure that permissions are set appropriately to allow the munin user to read these logs + +# Location of active instance of Xastir +XASTIRDIR="/home/USERNAME/.xastir" + +# Grab the station's callsign from Xastir config +# this currently does not derive the station call correctly when called by the server, but works using munin-run... +#STATION_CALL=`grep ^STATION_CALLSIGN:.* $XASTIRDIR/config/xastir.cnf | awk -F":" '{print $2}'` +STATION_CALL="" + +# Location of Xastir's logs (this can be a symlink to where Xastir actually writes the logs) +LOGDIR="/var/log/xastir/logs" + + +case $1 in + config) + cat <<'EOM' + +graph_title Xastir Packet Stats +graph_vlabel Packets +graph_category ham_radio +graph_scale no +graph_printf %.0lf + +igatetonet.label IGate - RF to Net +igatetonet.type COUNTER +igatetonet.min 0 +igatetonet.max 500 +igatetonet.LINE1 + +message.label Message RX +message.type COUNTER +message.min 0 +message.max 500 +message.draw LINE1 + +messagetx.label Message TX +messagetx.type COUNTER +messagetx.min 0 +messagetx.max 500 +messagetx.draw LINE1 + +net.label Net RX +net.type COUNTER +net.min 0 +net.max 500 +net.draw LINE1 + +nettx.label Net TX +nettx.type COUNTER +nettx.min 0 +nettx.max 500 +nettx.draw LINE1 + +tnc.label TNC RX +tnc.type COUNTER +tnc.min 0 +tnc.max 500 +tnc.draw LINE1 + +tnctx.label TNC TX +tnctx.type COUNTER +tnctx.min 0 +tnctx.max 500 +tnctx.draw LINE1 + +EOM + exit 0;; +esac + +# Parse logs for various values +IGATETONET=`cat $LOGDIR/igate.log | grep -e '^IGATE RF' | wc -l` +MESSAGE=`cat $LOGDIR/message.log | grep -v '^\#' | grep -v ^$STATION_CALL | wc -l` +MESSAGETX=`cat $LOGDIR/message.log | grep -v '^\#' | grep ^$STATION_CALL | wc -l` +NET=`cat $LOGDIR/net.log | grep -v '^\#' | grep -v ^$STATION_CALL | wc -l` +NETTX=`cat $LOGDIR/net.log | grep -v '^\#' | grep ^$STATION_CALL | wc -l` +TNC=`cat $LOGDIR/tnc.log | grep -v '^\#' | grep -v ^$STATION_CALL | wc -l` +TNCTX=`cat $LOGDIR/tnc.log | grep -v '^\#' | grep ^$STATION_CALL | wc -l` + +# Display values +echo "igatetonet.value $IGATETONET" +echo "message.value $MESSAGE" +echo "messagetx.value $MESSAGETX" +echo "net.value $NET" +echo "nettx.value $NETTX" +echo "tnc.value $TNC" +echo "tnctx.value $TNCTX" + diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index 40a4072b..7252dfe0 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -109,8 +109,10 @@ else fi L2_ACCESSES_TOTAL=`echo "$L2_HITS+$L2_MISSES" | $BC` -L2_HIT_RATIO_PERC=`echo "scale=2 ; (100*$L2_HITS/$L2_ACCESSES_TOTAL)" | $BC` -L2_MISS_RATIO_PERC=`echo "scale=2 ; (100*$L2_MISSES/$L2_ACCESSES_TOTAL)" | $BC` +if [ $L2_ACCESSES_TOTAL -gt 0 ]; then + L2_HIT_RATIO_PERC=`echo "scale=2 ; (100*$L2_HITS/$L2_ACCESSES_TOTAL)" | $BC` + L2_MISS_RATIO_PERC=`echo "scale=2 ; (100*$L2_MISSES/$L2_ACCESSES_TOTAL)" | $BC` +fi efficiency() { if [ "$1" = "config" ]; then diff --git a/plugins/zfs/zfs_usage_ b/plugins/zfs/zfs_usage_ new file mode 100755 index 00000000..d3700a6f --- /dev/null +++ b/plugins/zfs/zfs_usage_ @@ -0,0 +1,285 @@ +#!/usr/local/bin/perl +# -*- perl + +=pod + +=head1 NAME + +zfs_usage_ - Script to monitor zfs pool usage + +=head1 CONFIGURATION + +Create one symlink per zpool for exampe zfs_usage_system + +=head1 BUGS + +=head1 AUTHOR + +2012, Claudius Herder + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 LICENSE + +GPLv2 + +=cut + +use strict; +use warnings; +use Munin::Plugin; +need_multigraph(); + +my $filesystems; +my $zpool; +my $zpoolexec="/sbin/zpool"; +my $zfsexec="/sbin/zfs"; + +my $properties = { + available => "Read-only property that identifies the amount of disk" + ." space available to a file system and all its children," + ." assuming no other activity in the pool. Because disk" + ." space is shared within a pool, available space can be" + ." limited by various factors including physical pool size," + ." quotas, reservations, and other datasets within the" + ." pool.", + + quota => "Limits the amount of disk space a file system and its" + ." descendents can consume. This property enforces a" + ." hard limit on the amount of disk space used, including" + ." all space consumed by descendents, such as file systems" + ." and snapshots. Setting a quota on a descendent of a file" + ." system that already has a quota does not override the" + ." ancestor's quota, but rather imposes an additional" + ." limit. Quotas cannot be set on volumes, as the volsize" + ." property acts as an implicit quota.", + + referenced => "Read-only property that identifies the amount of data" + ." accessible by a dataset, which might or might not be" + ." shared with other datasets in the pool." + ." When a snapshot or clone is created, it initially" + ." references the same amount of disk space as the file" + ." system or snapshot it was created from, because its" + ." contents are identical.", + + refquota => "Sets the amount of disk space that a dataset can" + ." consume. This property enforces a hard limit on the" + ." amount of space used. This hard limit does not include" + ." disk space used by descendents, such as snapshots and" + ." clones.", + + refreservation => "Sets the minimum amount of disk space that is" + ." guaranteed to a dataset, not including descendents," + ." such as snapshots and clones. When the amount of disk" + ." space used is below this value, the dataset is treated as if" + ." it were taking up the amount of space specified by" + ." refreservation. The refreservation reservation is" + ." accounted for in the parent dataset's disk space used," + ." and counts against the parent dataset's quotas and" + ." reservations." + ." If refreservation is set, a snapshot is only allowed if" + ." enough free pool space is available outside of this" + ." reservation to accommodate the current number of" + ." referenced bytes in the dataset.", + + reservation => "Sets the minimum amount of disk space guaranteed to" + ." a file system and its descendents. When the amount of" + ." disk space used is below this value, the file system is" + ." treated as if it were using the amount of space specified" + ." by its reservation. Reservations are accounted for in the" + ." parent file system's disk space used, and count against" + ." the parent file system's quotas and reservations.", + + type => "Read-only property that identifies the dataset type as" + ." filesystem (file system or clone), volume, or" + ." snapshot.", + + used => "Read-only property that identifies the amount of disk" + ." space consumed by a dataset and all its descendents.", + + usedbychildren => "Read-only property that identifies the amount of disk" + ." space that is used by children of this dataset, which" + ." would be freed if all the dataset's children were" + ." destroyed. The property abbreviation is usedchild.", + + usedbydataset => "Read-only property that identifies the amount of disk" + ." space that is used by a dataset itself, which would be" + ." freed if the dataset was destroyed, after first destroying" + ." any snapshots and removing any refreservation" + ." reservations. The property abbreviation is usedds.", + + usedbyrefreservation=> "Read-only property that identifies the amount of disk" + ." space that is used by a refreservation set on a dataset," + ." which would be freed if the refreservation was" + ." removed.", + + usedbysnapshots => "Read-only property that identifies the amount of disk" + ." space that is consumed by snapshots of a dataset. In" + ." particular, it is the amount of disk space that would be" + ." freed if all of this dataset's snapshots were destroyed." + ." Note that this value is not simply the sum of the" + ." snapshots' used properties, because space can be" + ." shared by multiple snapshots.", + volsize => "For volumes, specifies the logical size of the volume.", +}; + +my @order = ( +"usedbydataset", +"usedbysnapshots", +"usedbyrefreservation", +"usedbychildren", +"available", +"quota", +"refquota", +"referenced", +"reservation", +"refreservation", +"used", +"volsize", +); + +sub do_collect { + my $fslist=(`$zfsexec list -H -r -o name $zpool`); + + my @params = join(',',( keys %{$properties} )); + + my $fsget="$zfsexec get -H -p @params"; + + foreach my $fsname (split(/\n/,$fslist)) { + foreach my $line (split(/\n/, `$fsget $fsname` )) { + my ($name, $key, $value, undef ) = (split(/\t/,$line)); + ($name =~ s/\//_/g); + $filesystems->{$name}->{$key}=$value; + } + } +} + + +sub do_config_fs { + my ($fs) = @_; + my $fs_slash = ($fs); + ($fs_slash =~ s/_/\//g); + + if ( $fs ne $zpool ) { + print "multigraph zfs_usage_$zpool.$fs\n"; + print "graph_title ZFS usage for $filesystems->{$fs}->{type} $fs_slash\n"; + print "graph_info This graph shows used bytes of $filesystems->{$fs}->{type} $fs_slash\n"; + } else { + print "multigraph zfs_usage_$zpool\n"; + print "graph_title ZFS usage for zpool $zpool\n"; + print "graph_info This graph shows used bytes of zpool $zpool\n"; + } + print "graph_args --base 1024 --lower-limit 0 --rigid\n"; + print "graph_vlabel bytes \n"; + print "graph_category zfs\n"; + print "graph_order @order\n"; + + foreach my $key ( keys %{$filesystems->{$fs}}) { + if ( $key ne "type" ) { + if ( $filesystems->{$fs}->{type} eq "volume" && $key =~ /quota/ ) { + } + elsif ($filesystems->{$fs}->{type} eq "filesystem" && $key eq "volsize") { + } + else { + print "$key.label $key\n"; + print "$key.min 0\n"; + print "$key.type GAUGE\n"; + print "$key.info $properties->{$key}\n"; + if ( $key =~ /quota|referenced|^(ref)*reservation|^used$|volsize/ ) { + print "$key.draw LINE3\n"; + } + else { + print "$key.draw AREASTACK\n"; + } + } + } + } +} + + +sub do_fetch_fs { + my ($fs) = @_; + + if ( $fs ne $zpool ) { + print "multigraph zfs_usage_$zpool.$fs\n"; + } else { + print "multigraph zfs_usage_$zpool\n"; + } + + foreach my $key ( keys %{$filesystems->{$fs}}) { + if ( $key ne "type" ) { + if ( $filesystems->{$fs}->{type} eq "volume" && $key =~ /quota/ ) { + } + elsif ($filesystems->{$fs}->{type} eq "filesystem" && $key eq "volsize") { + } + else { + print "$key.value $filesystems->{$fs}->{$key}\n"; + } + } + } +} + +sub do_config { + foreach my $fs ( sort keys %{$filesystems}) { + do_config_fs($fs); + } +} + +sub do_autoconf { + if (`which $zpoolexec 2>/dev/null` =~ m{^/}) { + print "yes\n"; + } else { + print "no ($zpoolexec could not be found)\n"; + } + exit 0; +} + + +sub do_suggest { + my $poollist=(`zpool list -H -o name`); + print "$poollist"; +} + +sub do_fetch { + foreach my $fs ( sort keys %{$filesystems}) { + do_fetch_fs($fs); + } +} + +sub do_setpool { + if ( $0 =~ /zfs_usage_$/) { + die ("Can't run without a symlinked name\n") + } + elsif ($0 =~ /zfs_usage_(.+)*$/) { + $zpool = $1; + } +} + +if ($ARGV[0]) { + if ($ARGV[0] eq "config") { + do_setpool(); + do_collect(); + do_config(); + exit 0; + } + elsif ($ARGV[0] eq "autoconf") { + do_autoconf(); + exit 0; + } + elsif ($ARGV[0] eq "suggest") { + do_suggest(); + exit 0; + } +} +else { + do_setpool(); + do_collect(); + do_fetch(); +} + +exit 0; + +__END__ diff --git a/tools/munin-node-c/.gitignore b/tools/munin-node-c/.gitignore new file mode 100644 index 00000000..91f33c2b --- /dev/null +++ b/tools/munin-node-c/.gitignore @@ -0,0 +1,4 @@ +# output files +/*.o +/munin-node-c +/plugins/* diff --git a/tools/munin-node-c/Makefile b/tools/munin-node-c/Makefile new file mode 100644 index 00000000..8081951c --- /dev/null +++ b/tools/munin-node-c/Makefile @@ -0,0 +1,24 @@ +CC=gcc +CFLAGS=-W -Wall -pedantic -Wextra -g -O2 +OBJS=main.o +LINKS= + +%.o: %.c + ${CC} ${CFLAGS} -c $< -o $@ +all: munin-node-c + +munin-node-c: ${OBJS} + ${CC} ${CFLAGS} $^ -o $@ +clean: + rm -f munin-node-c ${OBJS} ${LINKS} + rm -Rf plugins + +plugins: plugins/.munin-plugins-busybox.installed + +plugins/.munin-plugins-busybox.installed: + mkdir -p plugins + cd ../munin-plugins-busybox && $(MAKE) + cd plugins && for i in $$(find ../../munin-plugins-busybox -type l); do ln -s $$i; done + touch plugins/.munin-plugins-busybox.installed + +.PHONY: all clean plugins diff --git a/tools/munin-node-c/README b/tools/munin-node-c/README new file mode 100644 index 00000000..67c1544f --- /dev/null +++ b/tools/munin-node-c/README @@ -0,0 +1,28 @@ +This is a rewrite of munin node in C. + +Pro: +---- + +The purpose is multiple: + + * reducing resource usage for embedded plateforms, specially when paired + with the C rewrite of the core plugins. + + * no need for Perl + + * Everything runs from inetd. + +Cons: +----- + + * You lose flexibility + + It is compiled code, so you have to create binaries. Even one for each + architecture. + + * Not all the features are implemented + + - root uid is not supported. All plugins are run with a single user, usually nobody. + - no socket is opened. Everything runs from inetd. + +GPLv2 - (C) 2013 Steve SCHNEPP diff --git a/tools/munin-node-c/main.c b/tools/munin-node-c/main.c new file mode 100644 index 00000000..88a97561 --- /dev/null +++ b/tools/munin-node-c/main.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +char VERSION[] = "1.0.0"; + +int verbose = 0; + +char* host = ""; +char* plugin_dir = "plugins"; +char* spoolfetch_dir = ""; + +int main(int argc, char *argv[]) { + + int optch; + extern int opterr; + int optarg_len; + + char format[] = "vd:h:s:"; + + char line[LINE_MAX]; + + opterr = 1; + + while ((optch = getopt(argc, argv, format)) != -1) + switch (optch) { + case 'v': + verbose ++; + break; + case 'd': + optarg_len = strlen(optarg); + plugin_dir = (char *) malloc(optarg_len + 1); + strcpy(plugin_dir, optarg); + break; + case 'h': + optarg_len = strlen(optarg); + host = (char *) malloc(optarg_len + 1); + strcpy(host, optarg); + break; + case 's': + optarg_len = strlen(optarg); + spoolfetch_dir = (char *) malloc(optarg_len + 1); + strcpy(spoolfetch_dir, optarg); + break; + } + + /* get default hostname if not precised */ + if (! strlen(host)) { + host = (char *) malloc(HOST_NAME_MAX + 1); + gethostname(host, HOST_NAME_MAX); + } + + printf("# munin node at %s\n", host); + while (fgets(line, LINE_MAX, stdin) != NULL) { + char* cmd; + char* arg; + + line[LINE_MAX-1] = '\0'; + + cmd = strtok(line, " \t\n"); + if(cmd == NULL) + arg = NULL; + else + arg = strtok(NULL, " \t\n"); + + if (!cmd || strlen(cmd) == 0) { + printf("# empty cmd\n"); + } else if (strcmp(cmd, "version") == 0) { + printf("munin c node version: %s\n", VERSION); + } else if (strcmp(cmd, "nodes") == 0) { + printf("%s\n", host); + printf(".\n"); + } else if (strcmp(cmd, "quit") == 0) { + return(0); + } else if (strcmp(cmd, "list") == 0) { + DIR* dirp = opendir(plugin_dir); + struct dirent* dp; + while ((dp = readdir(dirp)) != NULL) { + char cmdline[LINE_MAX]; + char* plugin_filename = dp->d_name;; + + if (plugin_filename[0] == '.') { + /* No dotted plugin */ + continue; + } + + snprintf(cmdline, LINE_MAX, "%s/%s", plugin_dir, plugin_filename); + if (access(cmdline, X_OK) == 0) { + printf("%s ", plugin_filename); + } + } + printf("\n"); + closedir(dirp); + } else if ( + strcmp(cmd, "config") == 0 || + strcmp(cmd, "fetch") == 0 + ) { + char cmdline[LINE_MAX]; + pid_t pid; + if(arg == NULL) { + printf("# no plugin given\n"); + continue; + } + if(arg[0] == '.' || strchr(arg, '/')) { + printf("# invalid plugin character"); + continue; + } + snprintf(cmdline, LINE_MAX, "%s/%s", plugin_dir, arg); + if (access(cmdline, X_OK) == -1) { + printf("# unknown plugin: %s\n", arg); + continue; + } + if(0 == (pid = vfork())) { + execl(cmdline, arg, cmd, NULL); + /* according to vfork(2) we must use _exit */ + _exit(1); + } else if(pid < 0) { + printf("# fork failed\n"); + continue; + } else { + waitpid(pid, NULL, 0); + } + printf(".\n"); + } else if (strcmp(cmd, "cap") == 0) { + printf("cap "); + if (strlen(spoolfetch_dir)) { + printf("spool "); + } + printf("\n"); + } else if (strcmp(cmd, "spoolfetch") == 0) { + printf("# not implem yet cmd: %s\n", cmd); + } else { + printf("# unknown cmd: %s\n", cmd); + } + } + + return 0; +} diff --git a/tools/munin-plugins-busybox/.gitignore b/tools/munin-plugins-busybox/.gitignore new file mode 100644 index 00000000..5761abcf --- /dev/null +++ b/tools/munin-plugins-busybox/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/tools/munin-plugins-busybox/Makefile b/tools/munin-plugins-busybox/Makefile new file mode 100644 index 00000000..74de3046 --- /dev/null +++ b/tools/munin-plugins-busybox/Makefile @@ -0,0 +1,18 @@ +CC=gcc +CFLAGS=-W -Wall -pedantic -Wextra -g -O2 +OBJS=main.o common.o cpu.o entropy.o forks.o fw_packets.o interrupts.o \ + if_err_.o load.o open_files.o open_inodes.o processes.o swap.o threads.o \ + uptime.o +LINKS=cpu entropy forks fw_packets interrupts if_err_eth0 load open_files \ + open_inodes processes swap threads uptime + +%.o:%.c + ${CC} ${CFLAGS} -c $< -o $@ +all:munin-plugins-busybox + strip -s munin-plugins-busybox + for l in ${LINKS}; do test -f $$l || ln -s munin-plugins-busybox $$l; done +munin-plugins-busybox:${OBJS} + ${CC} ${CFLAGS} $^ -o $@ +clean: + rm -f munin-plugins-busybox ${OBJS} ${LINKS} +.PHONY:all clean diff --git a/tools/munin-plugins-busybox/README b/tools/munin-plugins-busybox/README new file mode 100644 index 00000000..4f8c56be --- /dev/null +++ b/tools/munin-plugins-busybox/README @@ -0,0 +1,36 @@ +What is this? +~~~~~~~~~~~~~ +This is a rewrite of commonly used munin plugins in C as a single binary. +The purpose is reducing resource usage: + * disk space: the binary is smaler than the plugins together + * more diskspace: it has no dependencies on other programs + * less forks: it does not fork internally + * faster startup: it doesn't start perl or shell + * less memory: just a small C program + * less file accesses: one binary for many plugins +This can be useful for machines with restricted resources like embedded +machines. + +What plugins are included? +~~~~~~~~~~~~~~~~~~~~~~~~~~ +cpu entropy forks fw_packets interrupts load open_files open_inodes +processes swap uptime + +Disadvantages? +~~~~~~~~~~~~~~ +You lose flexibility. You can no longer just edit the plugin and if you try +you have to be very careful not to break it. If you want to deploy this you +have to create one binary for each architecture. + +How to use? +~~~~~~~~~~~ +After compiling there will be binary munin-plugins-busybox. You can just +replace symlinks in /etc/munin/plugins/ with symlinks to this binary. + +License? +~~~~~~~~ +(C) 2008 Helmut Grohne +At your choice GPLv2 or GPLv3 applies to *.c *.h and Makefile + +# vim7:spelllang=en +# vim:textwidth=75 diff --git a/tools/munin-plugins-busybox/common.c b/tools/munin-plugins-busybox/common.c new file mode 100644 index 00000000..03638b10 --- /dev/null +++ b/tools/munin-plugins-busybox/common.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +extern char **environ; + +int writeyes(void) { + puts("yes"); + return 0; +} + +int autoconf_check_readable(const char *path) { + if(0 == access(path, R_OK)) + return writeyes(); + else { + printf("no (%s is not readable, errno=%d)\n", path, errno); + return 0; + } +} + +int getenvint(const char *name, int defvalue) { + const char *value; + value = getenv(name); + if(value == NULL) + return defvalue; + return atoi(value); +} + +const char *getenv_composed(const char *name1, const char *name2) { + char **p; + size_t len1 = strlen(name1), len2 = strlen(name2); + for(p = environ; *p; ++p) { + if(0 == strncmp(*p, name1, len1) && + 0 == strncmp(len1 + *p, name2, len2) && + (*p)[len1 + len2] == '=') + return len1 + len2 + 1 + *p; + } + return NULL; +} + +void print_warning(const char *name) { + const char *p; + p = getenv_composed(name, "_warning"); + if(p == NULL) + p = getenv("warning"); + if(p == NULL) + return; + + printf("%s.warning %s\n", name, p); +} + +void print_critical(const char *name) { + const char *p; + p = getenv_composed(name, "_critical"); + if(p == NULL) + p = getenv("critical"); + if(p == NULL) + return; + + printf("%s.critial %s\n", name, p); +} + +void print_warncrit(const char *name) { + print_warning(name); + print_critical(name); +} + +int fail(const char *message) { + fputs(message, stderr); + fputc('\n', stderr); + return 1; +} diff --git a/tools/munin-plugins-busybox/common.h b/tools/munin-plugins-busybox/common.h new file mode 100644 index 00000000..c35cac85 --- /dev/null +++ b/tools/munin-plugins-busybox/common.h @@ -0,0 +1,15 @@ +#ifndef COMMON_H +#define COMMON_H + +#define PROC_STAT "/proc/stat" + +int writeyes(void); +int autoconf_check_readable(const char *); +int getenvint(const char *, int); +const char *getenv_composed(const char *, const char *); +void print_warning(const char *); +void print_critical(const char *); +void print_warncrit(const char *); +int fail(const char *); + +#endif diff --git a/tools/munin-plugins-busybox/cpu.c b/tools/munin-plugins-busybox/cpu.c new file mode 100644 index 00000000..0cfa1d45 --- /dev/null +++ b/tools/munin-plugins-busybox/cpu.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include "common.h" + +#define SYSWARNING 30 +#define SYSCRITICAL 50 +#define USRWARNING 80 + +/* TODO: port support for env.foo_warning and env.foo_critical from mainline plugin */ + +int cpu(int argc, char **argv) { + FILE *f; + char buff[256], *s; + int ncpu=0, extinfo=0, scaleto100=0, hz; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + s = getenv("scaleto100"); + if(s && !strcmp(s, "yes")) + scaleto100=1; + + if(!(f=fopen(PROC_STAT, "r"))) + return fail("cannot open " PROC_STAT); + while(fgets(buff, 256, f)) { + if(!strncmp(buff, "cpu", 3)) { + if(isdigit(buff[3])) + ncpu++; + if(buff[3] == ' ') { + s = strtok(buff+4, " \t"); + for(extinfo=0;strtok(NULL, " \t");extinfo++) + ; + } + } + } + fclose(f); + + if(ncpu < 1 || extinfo < 4) + return fail("cannot parse " PROC_STAT); + + puts("graph_title CPU usage"); + if(extinfo >= 7) + puts("graph_order system user nice idle iowait irq softirq"); + else + puts("graph_order system user nice idle"); + if(scaleto100) + puts("graph_args --base 1000 -r --lower-limit 0 --upper-limit 100"); + else + printf("graph_args --base 1000 -r --lower-limit 0 --upper-limit %d\n", 100 * ncpu); + puts("graph_vlabel %\n" + "graph_scale no\n" + "graph_info This graph shows how CPU time is spent.\n" + "graph_category system\n" + "graph_period second\n" + "system.label system\n" + "system.draw AREA"); + printf("system.max %d\n", 100 * ncpu); + puts("system.min 0\n" + "system.type DERIVE"); + printf("system.warning %d\n", SYSWARNING * ncpu); + printf("system.critical %d\n", SYSCRITICAL * ncpu); + puts("system.info CPU time spent by the kernel in system activities\n" + "user.label user\n" + "user.draw STACK\n" + "user.min 0"); + printf("user.max %d\n", 100 * ncpu); + printf("user.warning %d\n", USRWARNING * ncpu); + puts("user.type DERIVE\n" + "user.info CPU time spent by normal programs and daemons\n" + "nice.label nice\n" + "nice.draw STACK\n" + "nice.min 0"); + printf("nice.max %d\n", 100 * ncpu); + puts("nice.type DERIVE\n" + "nice.info CPU time spent by nice(1)d programs\n" + "idle.label idle\n" + "idle.draw STACK\n" + "idle.min 0"); + printf("idle.max %d\n", 100 * ncpu); + puts("idle.type DERIVE\n" + "idle.info Idle CPU time"); + if(scaleto100) + printf("system.cdef system,%d,/\n" + "user.cdef user,%d,/\n" + "nice.cdef nice,%d,/\n" + "idle.cdef idle,%d,/\n", ncpu, ncpu, ncpu, ncpu); + if(extinfo >= 7) { + puts("iowait.label iowait\n" + "iowait.draw STACK\n" + "iowait.min 0"); + printf("iowait.max %d\n", 100 * ncpu); + puts("iowait.type DERIVE\n" + "iowait.info CPU time spent waiting for I/O operations to finish\n" + "irq.label irq\n" + "irq.draw STACK\n" + "irq.min 0"); + printf("irq.max %d\n", 100 * ncpu); + puts("irq.type DERIVE\n" + "irq.info CPU time spent handling interrupts\n" + "softirq.label softirq\n" + "softirq.draw STACK\n" + "softirq.min 0"); + printf("softirq.max %d\n", 100 * ncpu); + puts("softirq.type DERIVE\n" + "softirq.info CPU time spent handling \"batched\" interrupts"); + if(scaleto100) + printf("iowait.cdef iowait,%d,/\n" + "irq.cdef irq,%d,/\n" + "softirq.cdef softirq,%d,/\n", ncpu, ncpu, ncpu); + } + if(extinfo >= 8) { + puts("steal.label steal\n" + "steal.draw STACK\n" + "steal.min 0"); + printf("steal.max %d\n", 100 * ncpu); + puts("steal.type DERIVE\n" + "steal.info The time that a virtual CPU had runnable tasks, but the virtual CPU itself was not running"); + if(scaleto100) + printf("steal.cdef steal,%d,/\n", ncpu); + } + if(extinfo >= 9) { + puts("guest.label guest\n" + "guest.draw STACK\n" + "guest.min 0"); + printf("guest.max %d\n", 100 * ncpu); + puts("guest.type DERIVE\n" + "guest.info The time spent running a virtual CPU for guest operating systems under the control of the Linux kernel."); + if(scaleto100) + printf("guest.cdef guest,%d,/\n", ncpu); + } + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(PROC_STAT); + } + if(!(f=fopen(PROC_STAT, "r"))) + return fail("cannot open " PROC_STAT); + while(fgets(buff, 256, f)) { + hz = getenvint("HZ", 100); + if(!strncmp(buff, "cpu ", 4)) { + fclose(f); + if(!(s = strtok(buff+4, " \t"))) + break; + printf("user.value %ld\n", atol(s) * 100 / hz); + if(!(s = strtok(NULL, " \t"))) + break; + printf("nice.value %ld\n", atol(s) * 100 / hz); + if(!(s = strtok(NULL, " \t"))) + break; + printf("system.value %ld\n", atol(s) * 100 / hz); + if(!(s = strtok(NULL, " \t"))) + break; + printf("idle.value %ld\n", atol(s) * 100 / hz); + if(!(s = strtok(NULL, " \t"))) + return 0; + printf("iowait.value %ld\n", atol(s) * 100 / hz); + if(!(s = strtok(NULL, " \t"))) + return 0; + printf("irq.value %ld\n", atol(s) * 100 / hz); + if(!(s = strtok(NULL, " \t"))) + return 0; + printf("softirq.value %ld\n", atol(s) * 100 / hz); + if(!(s = strtok(NULL, " \t"))) + return 0; + printf("steal.value %ld\n", atol(s) * 100 / hz); + if(!(s = strtok(NULL, " \t"))) + return 0; + printf("guest.value %ld\n", atol(s) * 100 / hz); + return 0; + } + } + fclose(f); + return fail("no cpu line found in " PROC_STAT); +} diff --git a/tools/munin-plugins-busybox/entropy.c b/tools/munin-plugins-busybox/entropy.c new file mode 100644 index 00000000..2f7d998f --- /dev/null +++ b/tools/munin-plugins-busybox/entropy.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include "common.h" + +#define ENTROPY_AVAIL "/proc/sys/kernel/random/entropy_avail" + +int entropy(int argc, char **argv) { + FILE *f; + int entropy; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Available entropy\n" + "graph_args --base 1000 -l 0\n" + "graph_vlabel entropy (bytes)\n" + "graph_scale no\n" + "graph_category system\n" + "graph_info This graph shows the amount of entropy available in the system.\n" + "entropy.label entropy\n" + "entropy.info The number of random bytes available. This is typically used by cryptographic applications."); + print_warncrit("entropy"); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(ENTROPY_AVAIL); + } + if(!(f=fopen(ENTROPY_AVAIL, "r"))) + return fail("cannot open " ENTROPY_AVAIL); + if(1 != fscanf(f, "%d", &entropy)) { + fclose(f); + return fail("cannot read from " ENTROPY_AVAIL); + } + fclose(f); + printf("entropy.value %d\n", entropy); + return 0; +} diff --git a/tools/munin-plugins-busybox/forks.c b/tools/munin-plugins-busybox/forks.c new file mode 100644 index 00000000..fbbc5e59 --- /dev/null +++ b/tools/munin-plugins-busybox/forks.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include "common.h" + +int forks(int argc, char **argv) { + FILE *f; + char buff[256]; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Fork rate\n" + "graph_args --base 1000 -l 0 \n" + "graph_vlabel forks / ${graph_period}\n" + "graph_category processes\n" + "graph_info This graph shows the forking rate (new processes started).\n" + "forks.label forks\n" + "forks.type DERIVE\n" + "forks.min 0\n" + "forks.max 100000\n" + "forks.info The number of forks per second."); + print_warncrit("forks"); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(PROC_STAT); + } + if(!(f=fopen(PROC_STAT, "r"))) + return fail("cannot open " PROC_STAT); + while(fgets(buff, 256, f)) { + if(!strncmp(buff, "processes ", 10)) { + fclose(f); + printf("forks.value %s", buff+10); + return 0; + } + } + fclose(f); + return fail("no processes line found in " PROC_STAT); +} diff --git a/tools/munin-plugins-busybox/fw_packets.c b/tools/munin-plugins-busybox/fw_packets.c new file mode 100644 index 00000000..1bb9f0f0 --- /dev/null +++ b/tools/munin-plugins-busybox/fw_packets.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include "common.h" + +#define PROC_NET_SNMP "/proc/net/snmp" + +int fw_packets(int argc, char **argv) { + FILE *f; + char buff[1024], *s; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Firewall Throughput\n" + "graph_args --base 1000 -l 0\n" + "graph_vlabel Packets/${graph_period}\n" + "graph_category network\n" + "received.label Received\n" + "received.draw AREA\n" + "received.type DERIVE\n" + "received.min 0\n" + "forwarded.label Forwarded\n" + "forwarded.draw LINE2\n" + "forwarded.type DERIVE\n" + "forwarded.min 0"); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(PROC_NET_SNMP); + } + if(!(f=fopen(PROC_NET_SNMP, "r"))) + return fail("cannot open " PROC_NET_SNMP); + while(fgets(buff, 1024, f)) { + if(!strncmp(buff, "Ip: ", 4) && isdigit(buff[4])) { + fclose(f); + if(!(s = strtok(buff+4, " \t"))) + break; + if(!(s = strtok(NULL, " \t"))) + break; + if(!(s = strtok(NULL, " \t"))) + break; + printf("received.value %s\n", s); + if(!(s = strtok(NULL, " \t"))) + break; + if(!(s = strtok(NULL, " \t"))) + break; + if(!(s = strtok(NULL, " \t"))) + break; + printf("forwarded.value %s\n", s); + return 0; + } + } + fclose(f); + return fail("no ip line found in " PROC_NET_SNMP); +} diff --git a/tools/munin-plugins-busybox/if_err_.c b/tools/munin-plugins-busybox/if_err_.c new file mode 100644 index 00000000..8858a4a5 --- /dev/null +++ b/tools/munin-plugins-busybox/if_err_.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include "common.h" + +#define PROC_NET_DEV "/proc/net/dev" + +int if_err_(int argc, char **argv) { + char *interface; + size_t interface_len; + FILE *f; + char buff[256], *s; + int i; + + interface = basename(argv[0]); + if(strncmp(interface, "if_err_", 7) != 0) + return fail("if_err_ invoked with invalid basename"); + interface += 7; + interface_len = strlen(interface); + + if(argc > 1) { + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(PROC_NET_DEV); + if(!strcmp(argv[1], "suggest")) { + if(NULL == (f = fopen(PROC_NET_DEV, "r"))) + return 1; + while(fgets(buff, 256, f)) { + for(s=buff;*s == ' ';++s) + ; + i = 0; + if(!strncmp(s, "lo:", 3)) + continue; + if(!strncmp(s, "sit", 3)) { + for(i=3; isdigit(s[i]); ++i) + ; + if(s[i] == ':') + continue; + } + while(s[i] != ':' && s[i] != '\0') + ++i; + if(s[i] != ':') + continue; /* a header line */ + s[i] = '\0'; + puts(s); + } + fclose(f); + return 0; + } + if(!strcmp(argv[1], "config")) { + puts("graph_order rcvd trans"); + printf("graph_title %s errors\n", interface); + puts("graph_args --base 1000\n" + "graph_vlabel packets in (-) / out (+) per " + "${graph_period}\n" + "graph_category network"); + printf("graph_info This graph shows the amount of " + "errors on the %s network interface.\n", + interface); + puts("rcvd.label packets\n" + "rcvd.type COUNTER\n" + "rcvd.graph no\n" + "rcvd.warning 1\n" + "trans.label packets\n" + "trans.type COUNTER\n" + "trans.negative rcvd\n" + "trans.warning 1"); + print_warncrit("rcvd"); + print_warncrit("trans"); + return 0; + } + } + if(NULL == (f = fopen(PROC_NET_DEV, "r"))) + return 1; + while(fgets(buff, 256, f)) { + for(s=buff;*s == ' ';++s) + ; + if(0 != strncmp(s, interface, interface_len)) + continue; + s += interface_len; + if(*s != ':') + continue; + ++s; + + while(*s == ' ') + ++s; + + for(i=1;i<3;++i) { + while(isdigit(*s)) + ++s; + while(isspace(*s)) + ++s; + } + for(i=0;isdigit(s[i]);++i) + ; + printf("rcvd.value "); + fwrite(s, 1, i, stdout); + putchar('\n'); + s += i; + while(isspace(*s)) + ++s; + + for(i=4;i<11;++i) { + while(isdigit(*s)) + ++s; + while(isspace(*s)) + ++s; + } + for(i=0;isdigit(s[i]);++i) + ; + printf("trans.value "); + fwrite(s, 1, i, stdout); + putchar('\n'); + } + fclose(f); + return 0; +} diff --git a/tools/munin-plugins-busybox/interrupts.c b/tools/munin-plugins-busybox/interrupts.c new file mode 100644 index 00000000..7fcc6094 --- /dev/null +++ b/tools/munin-plugins-busybox/interrupts.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include "common.h" + +int interrupts(int argc, char **argv) { + FILE *f; + char buff[256]; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Interrupts and context switches\n" + "graph_args --base 1000 -l 0\n" + "graph_vlabel interrupts & ctx switches / ${graph_period}\n" + "graph_category system\n" + "graph_info This graph shows the number of interrupts and context switches on the system. These are typically high on a busy system.\n" + "intr.info Interrupts are events that alter sequence of instructions executed by a processor. They can come from either hardware (exceptions, NMI, IRQ) or software."); + puts("ctx.info A context switch occurs when a multitasking operatings system suspends the currently running process, and starts executing another.\n" + "intr.label interrupts\n" + "ctx.label context switches\n" + "intr.type DERIVE\n" + "ctx.type DERIVE\n" + "intr.max 100000\n" + "ctx.max 100000\n" + "intr.min 0\n" + "ctx.min 0"); + print_warncrit("intr"); + print_warncrit("ctx"); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(PROC_STAT); + } + if(!(f=fopen(PROC_STAT, "r"))) + return fail("cannot open " PROC_STAT); + while(fgets(buff, 256, f)) { + if(!strncmp(buff, "intr ", 5)) { + buff[5 + strcspn(buff + 5, " \t\n")] = '\0'; + printf("intr.value %s\n", buff+5); + } else if(!strncmp(buff, "ctxt ", 5)) { + buff[5 + strcspn(buff + 5, " \t\n")] = '\0'; + printf("ctx.value %s\n", buff+5); + } + } + fclose(f); + return 0; +} diff --git a/tools/munin-plugins-busybox/load.c b/tools/munin-plugins-busybox/load.c new file mode 100644 index 00000000..3b608ca2 --- /dev/null +++ b/tools/munin-plugins-busybox/load.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include "common.h" + +#define PROC_LOADAVG "/proc/loadavg" + +int load(int argc, char **argv) { + FILE *f; + float val; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Load average\n" + "graph_args --base 1000 -l 0\n" + "graph_vlabel load\n" + "graph_scale no\n" + "graph_category system\n" + "load.label load"); + print_warncrit("load"); + puts("graph_info The load average of the machine describes how many processes are in the run-queue (scheduled to run \"immediately\").\n" + "load.info 5 minute load average"); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return writeyes(); + } + if(!(f=fopen(PROC_LOADAVG, "r"))) + return fail("cannot open " PROC_LOADAVG); + if(1 != fscanf(f, "%*f %f", &val)) { + fclose(f); + return fail("cannot read from " PROC_LOADAVG); + } + fclose(f); + printf("load.value %.2f\n", val); + return 0; +} diff --git a/tools/munin-plugins-busybox/main.c b/tools/munin-plugins-busybox/main.c new file mode 100644 index 00000000..205731b2 --- /dev/null +++ b/tools/munin-plugins-busybox/main.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include "common.h" + +int cpu(int argc, char **argv); +int entropy(int argc, char **argv); +int forks(int argc, char **argv); +int fw_packets(int argc, char **argv); +int if_err_(int argc, char **argv); +int interrupts(int argc, char **argv); +int load(int argc, char **argv); +int open_files(int argc, char **argv); +int open_inodes(int argc, char **argv); +int processes(int argc, char **argv); +int swap(int argc, char **argv); +int threads(int argc, char **argv); +int uptime(int argc, char **argv); + +int busybox(int argc, char **argv) { + if(argc < 2) + return fail("missing parameter"); + if(0 != strcmp(argv[1], "listplugins")) + return fail("unknown parameter"); + puts("cpu\nentropy\nforks\nfw_packets\ninterrupts\nload\n" + "open_files\nopen_inodes\nswap\nthreads\nuptime"); + return 0; +} + +int main(int argc, char **argv) { + char *progname; + progname = basename(argv[0]); + switch(*progname) { + case 'c': + if(!strcmp(progname, "cpu")) + return cpu(argc, argv); + break; + case 'e': + if(!strcmp(progname, "entropy")) + return entropy(argc, argv); + break; + case 'f': + if(!strcmp(progname, "forks")) + return forks(argc, argv); + if(!strcmp(progname, "fw_packets")) + return fw_packets(argc, argv); + break; + case 'i': + if(!strcmp(progname, "interrupts")) + return interrupts(argc, argv); + if(!strncmp(progname, "if_err_", 6)) + return if_err_(argc, argv); + break; + case 'l': + if(!strcmp(progname, "load")) + return load(argc, argv); + break; + case 'm': + if(!strcmp(progname, "munin-plugins-busybox")) + return busybox(argc, argv); + break; + case 'o': + if(!strcmp(progname, "open_files")) + return open_files(argc, argv); + if(!strcmp(progname, "open_inodes")) + return open_inodes(argc, argv); + break; + case 'p': + if(!strcmp(progname, "processes")) + return processes(argc, argv); + break; + case 's': + if(!strcmp(progname, "swap")) + return swap(argc, argv); + break; + case 't': + if(!strcmp(progname, "threads")) + return threads(argc, argv); + break; + case 'u': + if(!strcmp(progname, "uptime")) + return uptime(argc, argv); + break; + } + return fail("unknown basename"); +} diff --git a/tools/munin-plugins-busybox/open_files.c b/tools/munin-plugins-busybox/open_files.c new file mode 100644 index 00000000..f83a9f6c --- /dev/null +++ b/tools/munin-plugins-busybox/open_files.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include "common.h" + +#define FS_FILE_NR "/proc/sys/fs/file-nr" + +/* TODO: support env.warning and friends after the upstream plugin is fixed */ + +int open_files(int argc, char **argv) { + FILE *f; + int alloc, freeh, avail; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + if(!(f=fopen(FS_FILE_NR, "r"))) + return fail("cannot open " FS_FILE_NR); + if(1 != fscanf(f, "%*d %*d %d", &avail)) { + fclose(f); + return fail("cannot read from " FS_FILE_NR); + } + fclose(f); + puts("graph_title File table usage\n" + "graph_args --base 1000 -l 0\n" + "graph_vlabel number of open files\n" + "graph_category system\n" + "graph_info This graph monitors the Linux open files table.\n" + "used.label open files\n" + "used.info The number of currently open files.\n" + "max.label max open files\n" + "max.info The maximum supported number of open " + "files. Tune by modifying " FS_FILE_NR + "."); + printf("used.warning %d\nused.critical %d\n", + (int)(avail*0.92), (int)(avail*0.98)); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(FS_FILE_NR); + } + if(!(f=fopen(FS_FILE_NR, "r"))) + return fail("cannot open " FS_FILE_NR); + if(3 != fscanf(f, "%d %d %d", &alloc, &freeh, &avail)) { + fclose(f); + return fail("cannot read from " FS_FILE_NR); + } + fclose(f); + printf("used.value %d\nmax.value %d\n", alloc-freeh, avail); + return 0; +} diff --git a/tools/munin-plugins-busybox/open_inodes.c b/tools/munin-plugins-busybox/open_inodes.c new file mode 100644 index 00000000..1c33e4de --- /dev/null +++ b/tools/munin-plugins-busybox/open_inodes.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include "common.h" + +#define FS_INODE_NR "/proc/sys/fs/inode-nr" + +int open_inodes(int argc, char **argv) { + FILE *f; + int nr, freen; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Inode table usage\n" + "graph_args --base 1000 -l 0\n" + "graph_vlabel number of open inodes\n" + "graph_category system\n" + "graph_info This graph monitors the Linux open inode table.\n" + "used.label open inodes\n" + "used.info The number of currently open inodes.\n" + "max.label inode table size\n" + "max.info The size of the system inode table. This is dynamically adjusted by the kernel."); + print_warncrit("used"); + print_warncrit("max"); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(FS_INODE_NR); + } + if(!(f=fopen(FS_INODE_NR, "r"))) + return fail("cannot open " FS_INODE_NR); + if(2 != fscanf(f, "%d %d", &nr, &freen)) { + fclose(f); + return fail("cannot read from " FS_INODE_NR); + } + fclose(f); + printf("used.value %d\nmax.value %d\n", nr-freen, nr); + return 0; +} diff --git a/tools/munin-plugins-busybox/processes.c b/tools/munin-plugins-busybox/processes.c new file mode 100644 index 00000000..8eb0b72e --- /dev/null +++ b/tools/munin-plugins-busybox/processes.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include "common.h" + +/* TODO: The upstream plugin does way more nowawdays. */ + +int processes(int argc, char **argv) { + DIR *d; + struct dirent *e; + char *s; + int n=0; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Number of Processes\n" + "graph_args --base 1000 -l 0 \n" + "graph_vlabel number of processes\n" + "graph_category processes\n" + "graph_info This graph shows the number of processes in the system.\n" + "processes.label processes\n" + "processes.draw LINE2\n" + "processes.info The current number of processes."); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return writeyes(); + } + if(!(d = opendir("/proc"))) + return fail("cannot open /proc"); + while((e = readdir(d))) { + for(s=e->d_name;*s;++s) + if(!isdigit(*s)) + break; + if(!*s) + ++n; + } + closedir(d); + printf("processes.value %d\n", n); + return 0; +} diff --git a/tools/munin-plugins-busybox/swap.c b/tools/munin-plugins-busybox/swap.c new file mode 100644 index 00000000..2c46fd49 --- /dev/null +++ b/tools/munin-plugins-busybox/swap.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include "common.h" + +int swap(int argc, char **argv) { + FILE *f; + char buff[256]; + int in, out; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Swap in/out\n" + "graph_args -l 0 --base 1000\n" + "graph_vlabel pages per ${graph_period} in (-) / out (+)\n" + "graph_category system\n" + "swap_in.label swap\n" + "swap_in.type DERIVE\n" + "swap_in.max 100000\n" + "swap_in.min 0\n" + "swap_in.graph no\n" + "swap_out.label swap\n" + "swap_out.type DERIVE\n" + "swap_out.max 100000\n" + "swap_out.min 0\n" + "swap_out.negative swap_in"); + print_warncrit("swap_in"); + print_warncrit("swap_out"); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return autoconf_check_readable(PROC_STAT); + } + if(!access("/proc/vmstat", F_OK)) { + in=out=0; + if(!(f=fopen("/proc/vmstat", "r"))) + return fail("cannot open /proc/vmstat"); + while(fgets(buff, 256, f)) { + if(!in && !strncmp(buff, "pswpin ", 7)) { + ++in; + printf("swap_in.value %s", buff+7); + } + else if(!out && !strncmp(buff, "pswpout ", 8)) { + ++out; + printf("swap_out.value %s", buff+8); + } + } + fclose(f); + if(!(in*out)) + return fail("no usable data on /proc/vmstat"); + return 0; + } else { + if(!(f=fopen(PROC_STAT, "r"))) + return fail("cannot open " PROC_STAT); + while(fgets(buff, 256, f)) { + if(!strncmp(buff, "swap ", 5)) { + fclose(f); + if(2 != sscanf(buff+5, "%d %d", &in, &out)) + return fail("bad data on " PROC_STAT); + printf("swap_in.value %d\nswap_out.value %d\n", in, out); + return 0; + } + } + fclose(f); + return fail("no swap line found in " PROC_STAT); + } +} diff --git a/tools/munin-plugins-busybox/threads.c b/tools/munin-plugins-busybox/threads.c new file mode 100644 index 00000000..c3389e60 --- /dev/null +++ b/tools/munin-plugins-busybox/threads.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include "common.h" + +int threads(int argc, char **argv) { + FILE *f; + char buff[256]; + const char *s; + int i, sum; + DIR *d; + struct dirent *e; + + if(argc > 1) { + if(!strcmp(argv[1], "autoconf")) { + i = getpid(); + sprintf(buff, "/proc/%d/status", i); + if(NULL == (f = fopen(buff, "r"))) + return fail("failed to open /proc/$$/status"); + while(fgets(buff, 256, f)) + if(!strncmp(buff, "Threads:", 8)) { + fclose(f); + return writeyes(); + } + fclose(f); + puts("no"); + return 0; + } + if(!strcmp(argv[1], "config")) { + puts("graph_title Number of threads\n" + "graph_vlabel number of threads\n" + "graph_category processes\n" + "graph_info This graph shows the number of threads.\n" + "threads.label threads\n" + "threads.info The current number of threads."); + return 0; + } + } + if(NULL == (d = opendir("/proc"))) + return fail("cannot open /proc"); + sum = 0; + while((e = readdir(d))) { + for(s=e->d_name;*s;++s) + if(!isdigit(*s)) + break; + if(*s) /* non-digit found */ + continue; + snprintf(buff, 256, "/proc/%s/status", e->d_name); + if(!(f = fopen(buff, "r"))) + continue; /* process has vanished */ + while(fgets(buff, 256, f)) { + if(strncmp(buff, "Threads:", 8)) + continue; + if(1 != sscanf(buff+8, "%d", &i)) { + fclose(f); + closedir(d); + return fail("failed to parse " + "/proc/somepid/status"); + } + sum += i; + } + fclose(f); + } + closedir(d); + printf("threads.value %d\n", sum); + return 0; +} diff --git a/tools/munin-plugins-busybox/uptime.c b/tools/munin-plugins-busybox/uptime.c new file mode 100644 index 00000000..6270acb6 --- /dev/null +++ b/tools/munin-plugins-busybox/uptime.c @@ -0,0 +1,30 @@ +#include +#include +#include "common.h" + +int uptime(int argc, char **argv) { + FILE *f; + float uptime; + if(argc > 1) { + if(!strcmp(argv[1], "config")) { + puts("graph_title Uptime\n" + "graph_args --base 1000 -l 0 \n" + "graph_vlabel uptime in days\n" + "uptime.label uptime\n" + "uptime.draw AREA"); + print_warncrit("uptime"); + return 0; + } + if(!strcmp(argv[1], "autoconf")) + return writeyes(); + } + if(!(f=fopen("/proc/uptime", "r"))) + return fail("cannot open /proc/uptime"); + if(1 != fscanf(f, "%f", &uptime)) { + fclose(f); + return fail("cannot read from /proc/uptime"); + } + fclose(f); + printf("uptime.value %.2f\n", uptime/86400); + return 0; +}