diff --git a/.travis.yml b/.travis.yml index 64c7c5d7..76144bd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: perl install: - sudo apt-get update - - sudo apt-get --no-install-recommends install devscripts python ruby php5-cli gawk ksh pylint + - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh pylint - sudo apt-get --no-install-recommends install pkg-config libdb-dev libvirt-dev libexpat-dev # - Munin/Plugin.pm is in "munin-node" on precise - sudo apt-get --no-install-recommends install munin-node diff --git a/plugins/apache/apache_average_time_last_n_requests b/plugins/apache/apache_average_time_last_n_requests old mode 100755 new mode 100644 index 709d43e0..f302eda4 --- a/plugins/apache/apache_average_time_last_n_requests +++ b/plugins/apache/apache_average_time_last_n_requests @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -# $Id$ # Author: Nicolas Mendoza - 2008-06-18 +# Raphaël Droz - 2016-01-08 # # Monitors the average time requests matching a custom regexp takes # For instance monitor time execution of files in http://example.com/foo/bar, @@ -16,12 +16,37 @@ # For instance: # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %T %v" # Check http://httpd.apache.org/docs/2.2/mod/mod_log_config.html#formats for more info +# +# Configurable variables +# fieldno - Override the default field number +# linecount - How many last request to consider +# Multiples instances for specific log files could be created by suffixing a configuration group. +# Eg: ln -s apache_average_time_last_n_requests apache_average_time_last_n_requests_vhost1 +# Then: +# [apache_average_time_last_n_requests] +# logfile_vhost1 /var/log/apache/vhost1.log +# +#%# family=auto use strict; -my $LAST_N_REQUESTS = 100000; # calculate based on this amount of requests -my $ACCESS_LOG_PATTERN = '/var/log/apache2/access.log.*'; # log pattern, if many it will take the last one. -my $TIME_FIELD_INDEX = -2; # second last field +$0 =~ /apache_average_time_last_n_requests_(.+)*$/; +my $name = $1; + +my $LAST_N_REQUESTS = exists $ENV{'linecount'} ? $ENV{'linecount'} : 100000; # calculate based on this amount of requests +my $TIME_FIELD_INDEX = exists $ENV{'fieldno'} ? $ENV{'fieldno'} : -2; # second last field +my $ACCESS_LOG_PATTERN; + +# log pattern, if globbing is used, it will take the last one. +if (! $name) { + $ACCESS_LOG_PATTERN = exists $ENV{'logfile'} ? $ENV{'logfile'} : '/var/log/apache2/access.log.*'; +} +elsif (exists $ENV{'logfile_' . $name}) { + $ACCESS_LOG_PATTERN = $ENV{'logfile_' . $name}; +} +else { + $ACCESS_LOG_PATTERN = '/var/log/apache2/access.log.*'; +} my $config =<< "CONFIG" graph_title Apache average seconds last $LAST_N_REQUESTS requests @@ -61,12 +86,12 @@ my $types = { my ($fields) = @_; my $script; ($script = $fields->[6]) =~ s/\?.*\z //mx; - return $script =~ m{ \.(png|jpe?g|jpg|gif|tiff|ilbm|tga) \z }mx; + return $script =~ m{ \.(png|jpe?g|gif|tiff|ilbm|tga) \z }mx; }, }, }; -if (defined(@ARGV) && ($ARGV[0] eq 'config')) { +if (@ARGV && $ARGV[0] eq 'config') { print $config; @@ -84,16 +109,21 @@ chomp $config_file; my @lines = `tail -n $LAST_N_REQUESTS "$config_file"`; +FOO: { foreach my $line (@lines) { foreach my $type (keys %{$types}) { my @fields = split /\s+/, $line; if ($types->{$type}->{'matches'}(\@fields)) { + if ($fields[$TIME_FIELD_INDEX] !~ m/^[0-9]+$/) { + last FOO; + } $types->{$type}->{'sum'} += $fields[$TIME_FIELD_INDEX]; $types->{$type}->{'lines'}++; } } } +} foreach my $type (keys %{$types}) { my $value = $types->{$type}->{'lines'} ? $types->{$type}->{'sum'} / $types->{$type}->{'lines'} : 'U'; printf "%s.value %s\n", ($type, $value); diff --git a/plugins/apache/apache_cache_disk_count b/plugins/apache/apache_cache_disk_count new file mode 100755 index 00000000..e3f1c807 --- /dev/null +++ b/plugins/apache/apache_cache_disk_count @@ -0,0 +1,123 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + +Munin plugin to monitor apache mod_cache_disk usage. + +=head1 CONFIGURATION + +[apache_cache_disk_count] + user www-data + env.cache_path /var/cache/apache2/mod_cache_disk + env.strings css js + env.label_cs CSS + env.colour_css FFFF00 + env.label_js JS + env.colour_js FF0000 + +=head1 AUTHOR + +Raphaël Droz + +=head1 LICENSE + +GPLv2 + +=head1 MAGICK MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +. $MUNIN_LIBDIR/plugins/plugin.sh + +my_cache_path="${cache_path:-/var/cache/apache2/mod_cache_disk}" +declare -a fieldnames labels colours + +getenvdata() { + let during_autoconf=0 + [[ $1 == -v ]] && during_autoconf=1 + arg=( "${strings:-}" ) + for i in ${arg[*]}; do + if [[ ! $i =~ ^[a-zA-Z0-9]+$ ]]; then + (( $during_autoconf )) && echo "no ($i isn't a valid fixed-string)" + ok=0 + continue + fi + label=label_$i + if [[ -z "${!label}" ]]; then + (( $during_autoconf )) && echo "no ($i isn't given a label)" + ok=0 + continue + fi + colour=colour_$i + if [[ -z "${!colour}" ]]; then + (( $during_autoconf )) && echo "no ($i isn't given a colour)" + ok=0 + continue + fi + + fieldnames+=($i) + labels+=("${!label}") + colours+=("${!colour}") + done +} + +if [[ $1 == autoconf ]]; then + let ok=1 + + [[ -z $strings ]] && echo "no strings to monitor defined" && ok=0 + ! type -P htcacheclean &>/dev/null && echo "can't find htcacheclean" && ok=0 + ! test -d "$my_cache_path" && echo "cache_path \"$cache_path\" is not readable" && ok=0 + + getenvdata -v + + (( ${#fieldnames[*]} == 0 )) && echo "no (no valid strings)" && ok=0 + (( $ok == 1 )) && echo yes + exit 0 +fi + +getenvdata + +if [[ $1 == config ]]; then +cat <) { + chomp; + my @values = split( /\|/, $_ ); + + my %logentry; + @logentry{@keys} = @values; + + $stats->{'bytes'}{ $logentry{'direction'} } += $logentry{'size'}; + $stats->{'requests'}{ $logentry{'direction'} }++; + } + + # Close log + $pos = tail_close($fh); + + write_state($pos, $stats); + + return $stats; +} + +sub print_autoconf{ + my $logfile = shift; + if ( open(my $fh, '<', $logfile) ) { + print "yes\n"; + } + else { + printf "no (could not open %s)\n", $logfile; + } +} + +sub print_config{ + my $stats = shift; + + print << 'EOC'; +multigraph acng_bytes +graph_title Apt-Cacher NG bytes +graph_order origin client +graph_vlabel bytes per ${graph_period} +graph_info Bytes transferred between origin, apt-cacher-ng and clients +origin.info bytes transferred between origin and apt-cacher-ng +origin.label origin +origin.type DERIVE +origin.min 0 +client.info bytes transferred between apt-cacher-ng and clients +client.label client +client.type DERIVE +client.min 0 +EOC + print << "EOV" if $ENV{'MUNIN_CAP_DIRTYCONFIG'}; +origin.value $stats->{bytes}{I} +client.value $stats->{bytes}{O} +EOV + + print << 'EOC'; + +multigraph acng_requests +graph_title Apt-Cacher NG requests +graph_order origin client +graph_vlabel requests per ${graph_period} +graph_info Requests from clients to apt-cacher-ng, and from apt-cacher-ng to origin +origin.info requests from apt-cacher-ng to origin +origin.label origin +origin.type DERIVE +origin.min 0 +client.info requests from clients to apt-cacher-ng +client.label client +client.type DERIVE +client.min 0 +EOC + + print << "EOV" if $ENV{'MUNIN_CAP_DIRTYCONFIG'}; +origin.value $stats->{requests}{I} +client.value $stats->{requests}{O} +EOV + +} + +sub print_values{ + my $stats = shift; + + print << "EOV"; +multigraph acng_bytes +origin.value $stats->{bytes}{I} +client.value $stats->{bytes}{O} + +multigraph acng_requests +origin.value $stats->{requests}{I} +client.value $stats->{requests}{O} +EOV +} + +if ($ARGV[0] and $ARGV[0] eq 'autoconf') { + print_autoconf($logfile); +} +elsif ($ARGV[0] and $ARGV[0] eq 'config') { + my $stats = parse_logfile($logfile); + print_config($stats); +} +else { + my $stats = parse_logfile($logfile); + print_values($stats); +} diff --git a/plugins/apt/deb_packages/README.md b/plugins/apt/deb_packages/README.md index 1ccffd40..56b60ff3 100644 --- a/plugins/apt/deb_packages/README.md +++ b/plugins/apt/deb_packages/README.md @@ -29,9 +29,13 @@ check out this git repository from aptitude install python-apt git clone git://github.com/munin-monitoring/contrib.git cd contrib/plugins/apt/deb_packages - sudo cp deb_packages.py /etc/munin/plugins + sudo cp deb_packages.py /etc/munin/plugins/deb_packages sudo cp deb_packages.munin-conf /etc/munin/plugin-conf.d/deb_packages +Verify the installation by + + sudo munin-run deb_packages + ### Configuration If you copied deb_packages.munin-conf to plugin-conf.d you have a starting point. A typical configuration looks like this diff --git a/plugins/backuppc/backuppc b/plugins/backuppc/backuppc index c7db2299..222771e2 100755 --- a/plugins/backuppc/backuppc +++ b/plugins/backuppc/backuppc @@ -4,6 +4,10 @@ # [backuppc] # user backuppc # env.pcdir /var/lib/BackupPC/pc +# env.full_warning 10 # warn if FULL backup older than N days +# env.full_critical 20 # critical if FULL backup older than N days +# env.incr_warning 1 # warn if INCR backup older than N days +# env.incr_critical 3 # critical if INCR backup older than N days # #%# family=backuppc #%# capabilities=autoconf @@ -42,6 +46,18 @@ if [ "$1" = "config" ]; then do echo "$(clean_fieldname ${h})_full.label $(clean_fieldname ${h}) Full" echo "$(clean_fieldname ${h})_incr.label $(clean_fieldname ${h}) Incr" + if [ -n "$full_warning" ]; then + echo "$(clean_fieldname ${h})_full.warning $full_warning" + fi + if [ -n "$incr_warning" ]; then + echo "$(clean_fieldname ${h})_incr.warning $incr_warning" + fi + if [ -n "$full_critical" ]; then + echo "$(clean_fieldname ${h})_full.critical $full_critical" + fi + if [ -n "$incr_critical" ]; then + echo "$(clean_fieldname ${h})_incr.critical $incr_critical" + fi done exit 0 @@ -150,3 +166,5 @@ one per row. The columns are: mangle Set if this backup has mangled file names and attributes. Always true for backups in v1.4.0 and above. False for all backups prior to v1.4.0. + +__END__ diff --git a/plugins/balanceng/bng b/plugins/balanceng/bng new file mode 100755 index 00000000..5515dbd9 --- /dev/null +++ b/plugins/balanceng/bng @@ -0,0 +1,274 @@ +#!/usr/bin/perl + +# munin plugin for BalanceNG +# https://www.inlab.de/load-balancer/index.html +# +# (w) 2015 by Rhonda D'Vine +# (c) 2015 by strg.at GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 2 dated June, +# 1991. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +use warnings; +use strict; + +use Munin::Plugin; + +my $host; + +my %servers; +my %targets; + +# we support autoconf +#%# family=auto +#%# capabilities=autoconf suggest + +# Check for multigraph capabilities +need_multigraph(); + +# Handle munin 'autoconf' command +do_autoconf() if ( $ARGV[0] && $ARGV[0] eq 'autoconf' ); + +# Fetch data values +do_fetch_values(); + +# Handle munin 'config' command +# This can only be done after getting the data +if ( defined $ARGV[0] && $ARGV[0] eq 'config' ) { + do_config(); +} + + +generate_output(); + +exit 0; + +sub do_autoconf { # {{{ check if we can offer autoconf + print (-x '/sbin/bng' ? "yes\n" : "no\n"); + exit 0; +} # }}} + +sub do_fetch_values { # {{{ fetch all the names and values needed + + ## MIBs from https://www.inlab.de/load-balancer/BalanceNG-V3-Manual.pdf + + # node name + open READ, "/sbin/bng -g .1.3.6.1.4.1.2771.1.5 |" || die "$0: can't fork bng: $!\n"; + ; + ; + $host = ; + chomp $host; + close READ; + + my $next; + + # server names + $next = '.1.3.6.1.4.1.2771.1.60.3'; + while (open READ, "/sbin/bng -n $next |") { + $next = ; + chomp $next; + + if ($next !~ /^\.1\.3\.6\.1\.4\.1\.2771\.1\.60\.3/) { + close READ; + last; + } + + ; + my $name = ; + chomp $name; + push @{$servers{name}}, clean_fieldname($name); + + close READ; + } + + # server sent bytes + $next = '.1.3.6.1.4.1.2771.1.60.15'; + while (open READ, "/sbin/bng -n $next |") { + $next = ; + chomp $next; + + if ($next !~ /^\.1\.3\.6\.1\.4\.1\.2771\.1\.60\.15/) { + close READ; + last; + } + + ; + my $name = ; + chomp $name; + push @{$servers{sent}}, $name; + $servers{senttotal} += $name; + + close READ; + } + + # server recv bytes + $next = '.1.3.6.1.4.1.2771.1.60.17'; + while (open READ, "/sbin/bng -n $next |") { + $next = ; + chomp $next; + + if ($next !~ /^\.1\.3\.6\.1\.4\.1\.2771\.1\.60\.17/) { + close READ; + last; + } + + ; + my $name = ; + chomp $name; + push @{$servers{recv}}, $name; + $servers{recvtotal} += $name; + + close READ; + } + + # target names + $next = '.1.3.6.1.4.1.2771.1.70.3'; + while (open READ, "/sbin/bng -n $next |") { + $next = ; + chomp $next; + + if ($next !~ /^\.1\.3\.6\.1\.4\.1\.2771\.1\.70\.3/) { + close READ; + last; + } + + ; + my $name = ; + chomp $name; + push @{$targets{name}}, clean_fieldname($name); + + close READ; + } + + # target sent bytes + $next = '.1.3.6.1.4.1.2771.1.70.27'; + while (open READ, "/sbin/bng -n $next |") { + $next = ; + chomp $next; + + if ($next !~ /^\.1\.3\.6\.1\.4\.1\.2771\.1\.70\.27/) { + close READ; + last; + } + + ; + my $name = ; + chomp $name; + push @{$targets{sent}}, $name; + $targets{senttotal} += $name; + + close READ; + } + + # target recv bytes + $next = '.1.3.6.1.4.1.2771.1.70.29'; + while (open READ, "/sbin/bng -n $next |") { + $next = ; + chomp $next; + + if ($next !~ /^\.1\.3\.6\.1\.4\.1\.2771\.1\.70\.29/) { + close READ; + last; + } + + ; + my $name = ; + chomp $name; + push @{$targets{recv}}, $name; + $targets{recvtotal} += $name; + + close READ; + } + +} # }}} + +sub do_config { # {{{ write out the configuration + print <<"EOF"; +multigraph bng +graph_title Bng Throughput +graph_order sent recv +graph_args --base 1000 -l 0 +graph_vlabel Packets/\${graph_period} +graph_category network +graph_info this graph shows the outbound traffic for $host + +sent.label Sent +sent.draw AREA +sent.type DERIVE +sent.min 0 +recv.label Received +recv.draw AREA +recv.type DERIVE +recv.min 0 + +EOF + + foreach my $interface (@{$servers{name}}, @{$targets{name}}) { + print <<"EOF"; +multigraph bng.${interface} + +graph_title Interface $interface traffic +graph_order sent recv +graph_args --base 1000 -l 0 +graph_vlabel Packets/\${graph_period} +graph_category network +graph_info this graph shows the total traffic for ${interface} + +sent.label ${interface}_Sent +sent.draw AREA +sent.type DERIVE +sent.min 0 +recv.label ${interface}_Received +recv.draw AREA +recv.type DERIVE +recv.min 0 + +EOF + } + + exit 0; +} # }}} + +sub generate_output { # {{{ write out the data + print <<"EOF"; +multigraph bng +sent.value $servers{senttotal} +recv.value $servers{recvtotal} +EOF + + my $i; + $i = 0; + foreach my $interface (@{$servers{name}}) { + print <<"EOF"; +multigraph bng.${interface} +sent.value $servers{sent}[$i] +recv.value $servers{recv}[$i] +EOF + $i++; + } + + $i = 0; + foreach my $interface (@{$targets{name}}) { + print <<"EOF"; +multigraph bng.${interface} +sent.value $targets{sent}[$i] +recv.value $targets{recv}[$i] +EOF + $i++; + } + +} # }}} + +# vim:fdm=marker: diff --git a/plugins/cyrus/cyrus-imapd b/plugins/cyrus/cyrus-imapd index faaba8cc..e2b54e3a 100755 --- a/plugins/cyrus/cyrus-imapd +++ b/plugins/cyrus/cyrus-imapd @@ -66,11 +66,14 @@ GPLv2 =cut # IMAP Configuration Directory -CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null) -PROCDIR="${CONFIGDIR}/proc" +PROCDIR=$(awk -F : '/^proc_path:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null) +if [ "x${PROCDIR}x" = "xx" ]; then + CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null) + PROCDIR="${CONFIGDIR}/proc" +fi if [ "$1" = "autoconf" ]; then - if [ "x${CONFIGDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then + if [ "x${PROCDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then echo yes else echo "no (no cyrus-imapd procdir found)" @@ -79,7 +82,7 @@ if [ "$1" = "autoconf" ]; then fi # Check if we actually got some sensible data -if [ "x${CONFIGDIR}x" = "xx" ]; then +if [ "x${PROCDIR}x" = "xx" ]; then exit 1 fi diff --git a/plugins/disk/xfs b/plugins/disk/xfs index 94a7cb53..02fc827b 100755 --- a/plugins/disk/xfs +++ b/plugins/disk/xfs @@ -46,6 +46,7 @@ my %runtime_stats = ( "vnodes" => ["vn_active","vn_alloc","vn_get","vn_hold","vn_rele","vn_reclaim","vn_remove","vn_free"], "buf" => ["xb_get","xb_create","xb_get_locked","xb_get_locked_waited","xb_busy_locked","xb_miss_locked","xb_page_retries","xb_page_found","xb_get_read"], "ibt2" => ["xs_ibt_2_lookup","xs_ibt_2_compare","xs_ibt_2_insrec","xs_ibt_2_delrec","xs_ibt_2_newroot","xs_ibt_2_killroot","xs_ibt_2_increment","xs_ibt_2_decrement","xs_ibt_2_lshift","xs_ibt_2_rshift","xs_ibt_2_split","xs_ibt_2_join","xs_ibt_2_alloc","xs_ibt_2_free","xs_ibt_2_moves"], + "fibt2" => ["xs_fibt_2_lookup","xs_fibt_2_compare","xs_fibt_2_insrec","xs_fibt_2_delrec","xs_fibt_2_newroot","xs_fibt_2_killroot","xs_fibt_2_increment","xs_fibt_2_decrement","xs_fibt_2_lshift","xs_fibt_2_rshift","xs_fibt_2_split","xs_fibt_2_join","xs_fibt_2_alloc","xs_fibt_2_free","xs_fibt_2_moves"], "bmbt2" => ["xs_bmbt_2_lookup","xs_bmbt_2_compare","xs_bmbt_2_insrec","xs_bmbt_2_delrec","xs_bmbt_2_newroot","xs_bmbt_2_killroot","xs_bmbt_2_increment","xs_bmbt_2_decrement","xs_bmbt_2_lshift","xs_bmbt_2_rshift","xs_bmbt_2_split","xs_bmbt_2_join","xs_bmbt_2_alloc","xs_bmbt_2_free","xs_bmbt_2_moves"], "abtc2" => ["xs_abtc_2_lookup","xs_abtc_2_compare","xs_abtc_2_insrec","xs_abtc_2_delrec","xs_abtc_2_newroot","xs_abtc_2_killroot","xs_abtc_2_increment","xs_abtc_2_decrement","xs_abtc_2_lshift","xs_abtc_2_rshift","xs_abtc_2_split","xs_abtc_2_join","xs_abtc_2_alloc","xs_abtc_2_free","xs_abtc_2_moves"], "abtb2" => ["xs_abtb_2_lookup","xs_abtb_2_compare","xs_abtb_2_insrec","xs_abtb_2_delrec","xs_abtb_2_newroot","xs_abtb_2_killroot","xs_abtb_2_increment","xs_abtb_2_decrement","xs_abtb_2_lshift","xs_abtb_2_rshift","xs_abtb_2_split","xs_abtb_2_join","xs_abtb_2_alloc","xs_abtb_2_free","xs_abtb_2_moves"], diff --git a/plugins/docker/docker_cpu b/plugins/docker/docker_cpu new file mode 100755 index 00000000..63165a11 --- /dev/null +++ b/plugins/docker/docker_cpu @@ -0,0 +1,120 @@ +#!/usr/bin/perl -w +# -*- perl -*- + +=head1 NAME + +docker_cpu - Munin plugin to monitor docker container CPU usage. + +=head1 APPLICABLE SYSTEMS + +Should work on any Linux system that has docker support. + +=head1 CONFIGURATION + +Root privilege required to execute docker command. + +1. Create a new file named "docker" inside the folder /etc/munin/plugin-conf.d/ +2. Docker file content: + +[docker_cpu] +user root + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + + v.0.1 + +=head1 AUTHOR + +Copyright (C) 2015 Samuel Cantero. +Email: scanterog at gmail dot com + +=head1 LICENSE + +GPLv3 + +=cut + +my $docker=`which docker`; + +if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { + if ($docker) { + print "yes\n"; + exit 0; + } + else{ + print "no (Docker has not been found)\n"; + exit 0; + } +} + +$docker =~ s/\s+$//; + +my @containers = split "\n" , `$docker ps --no-trunc=true`; +my $result; + +for my $i (1 .. $#containers) +{ + my @fields = split / +/, $containers[$i]; + my $id = $fields[0]; + my $name = $fields[$#fields]; + # manage container name containing arithmetic operators and dots. E.g, my-container. + $name =~ s/[-\+*\/\.]/_/g; + # truncate container name with "," character. + $name =~ s/,.*//g; + if (open(my $file, '<', "/sys/fs/cgroup/cpuacct/docker/$id/cpuacct.usage")) + { + my $total_cpu_ns = <$file>; + $total_cpu_ns =~ s/\s+$//; + close $file; + if (open($file, '<', "/sys/fs/cgroup/cpuacct/docker/$id/cpuacct.usage_percpu")) + { + my @ncpu = split / /, <$file>; + close $file; + push @result, {'name'=>$name, 'total_cpu_ns'=>$total_cpu_ns, 'ncpu'=>$#ncpu}; + } + } +} + +if (defined $ARGV[0] and $ARGV[0] eq "config") +{ + my $nanoSecondsInSecond=1000000000; + my $graphlimit = $result[0]{'ncpu'}; + foreach(@result){ + if ($$_{'ncpu'} > $graphlimit){ + $graphlimit = $$_{'ncpu'}; + } + } + $graphlimit = $graphlimit * 100; + print "graph_title Docker container CPU usage\n"; + print "graph_args --base 1000 -r --lower-limit 0 --upper-limit $graphlimit\n"; + print "graph_vlabel %\n"; + print "graph_scale no\n"; + print "graph_period second\n"; + print "graph_category Docker\n"; + print "graph_info This graph shows docker container CPU usage.\n"; + + foreach(@result) + { + print "$$_{'name'}.label $$_{'name'}\n"; + print "$$_{'name'}.draw LINE2\n"; + print "$$_{'name'}.min 0\n"; + print "$$_{'name'}.type DERIVE\n"; + print "$$_{'name'}.cdef $$_{'name'},$nanoSecondsInSecond,/\n"; + } + exit 0; +} + +# Note: Counters/derive need to report integer values. + +foreach(@result) +{ + $tcpu = ($$_{'total_cpu_ns'}*100); #to percentage + print "$$_{'name'}.value $tcpu\n"; +} + +# vim:syntax=perl diff --git a/plugins/docker/docker_memory b/plugins/docker/docker_memory new file mode 100755 index 00000000..1d848043 --- /dev/null +++ b/plugins/docker/docker_memory @@ -0,0 +1,98 @@ +#!/usr/bin/perl -w +# -*- perl -*- + +=head1 NAME + +docker_memory - Munin plugin to monitor docker container memory usage. + +=head1 APPLICABLE SYSTEMS + +Should work on any Linux system that has docker support. + +=head1 CONFIGURATION + +Root privilege required to execute docker command. + +1. Create a new file named "docker" inside the folder /etc/munin/plugin-conf.d/ +2. Docker file content: + +[docker_memory] +user root + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + + v.0.1 + +=head1 AUTHOR + +Copyright (C) 2015 Samuel Cantero. +Email: scanterog at gmail dot com + +=head1 LICENSE + +GPLv3 + +=cut + +my $docker=`which docker`; + +if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" ) { + if ($docker) { + print "yes\n"; + exit 0; + } + else{ + print "no (Docker has not been found)\n"; + exit 0; + } +} + +$docker =~ s/\s+$//; + +my @containers = split "\n" , `$docker ps --no-trunc=true`; +my $result; + +for my $i (1 .. $#containers) +{ + my @fields = split / +/, $containers[$i]; + my $id = $fields[0]; + my $name = $fields[$#fields]; + # manage container name containing arithmetic operators and dots. E.g, my-container. + $name =~ s/[-\+*\/\.]/_/g; + # truncate container name with "," character. + $name =~ s/,.*//g; + if (open(my $file, '<', "/sys/fs/cgroup/memory/docker/$id/memory.usage_in_bytes")) + { + my $memory_bytes = <$file>; + $memory_bytes =~ s/\s+$//; + push @result, {'name'=>$name, 'memory_bytes'=>$memory_bytes}; + } +} + +if (defined $ARGV[0] and $ARGV[0] eq "config") +{ + print "graph_title Docker container memory usage\n"; + print "graph_args --base 1024 -l 0\n"; + print "graph_vlabel Bytes\n"; + print "graph_category Docker\n"; + print "graph_info This graph shows docker container memory usage.\n"; + + foreach(@result) + { + print "$$_{'name'}.label $$_{'name'}\n"; + print "$$_{'name'}.draw LINE2\n"; + } + exit 0; +} + +foreach(@result) +{ + print "$$_{'name'}.value $$_{'memory_bytes'}\n"; +} + +# vim:syntax=perl diff --git a/plugins/docker/example_graphs/docker_cpu_usage.png b/plugins/docker/example_graphs/docker_cpu_usage.png new file mode 100644 index 00000000..c09b6917 Binary files /dev/null and b/plugins/docker/example_graphs/docker_cpu_usage.png differ diff --git a/plugins/docker/example_graphs/docker_memory_usage.png b/plugins/docker/example_graphs/docker_memory_usage.png new file mode 100644 index 00000000..04ab75ed Binary files /dev/null and b/plugins/docker/example_graphs/docker_memory_usage.png differ diff --git a/plugins/ftp/proftpd_bytes b/plugins/ftp/proftpd_bytes index e1cdb31d..85d3ef39 100755 --- a/plugins/ftp/proftpd_bytes +++ b/plugins/ftp/proftpd_bytes @@ -20,7 +20,7 @@ mktempfile () { mktemp -t $1 } -LOGFILE=${logfile:-/var/log/xferlog} +LOGFILE=${logfile:-/var/log/proftpd/xferlog} LOGTAIL=${logtail:-`which logtail`} STATEFILE=/var/lib/munin/plugin-state/xferlog-bytes.offset diff --git a/plugins/ftp/proftpd_count b/plugins/ftp/proftpd_count index 44282167..aa9c9d3f 100755 --- a/plugins/ftp/proftpd_count +++ b/plugins/ftp/proftpd_count @@ -20,7 +20,7 @@ mktempfile () { mktemp -t $1 } -LOGFILE=${logfile:-/var/log/xferlog} +LOGFILE=${logfile:-/var/log/proftpd/xferlog} LOGTAIL=${logtail:-`which logtail`} STATEFILE=/var/lib/munin/plugin-state/xferlog-count.offset diff --git a/plugins/gunicorn/gunicorn_memory_status b/plugins/gunicorn/gunicorn_memory_status index 49e8719c..4cdbb58b 100755 --- a/plugins/gunicorn/gunicorn_memory_status +++ b/plugins/gunicorn/gunicorn_memory_status @@ -22,7 +22,7 @@ """ -import sys, os +import sys, os, re from subprocess import check_output # set path to your gunicorn pid @@ -44,11 +44,9 @@ class GunicornMemoryStatus(): except: raise Exception("Couldn't read gunicorn pid information") - def print_total_memory(self): print ('total_memory.value %d' % self._get_total_memory()) - def _get_master_pid(self): master_pid_file = open(GUNICORN_PID_PATH) self.master_pid = master_pid_file.read().rstrip() @@ -74,7 +72,18 @@ class GunicornMemoryStatus(): return worker_memory_usage def print_config(): - print "graph_title Gunicorn - Memory Usage" + instance = None + name = os.path.basename(sys.argv[0]) + if name != "gunicorn_memory_status": + for r in ("^gunicorn_(.*?)_memory_status$", "^gunicorn_memory_status_(.*?)$"): + m = re.match(r, name, re.IGNORECASE) + if m: + instance = m.group(1) + break + graph_title = "graph_title Gunicorn - Memory Usage" + if instance: + graph_title = "%s - %s" % (graph_title, instance) + print graph_title print "graph_args --base 1024 -l 0" print "graph_vlabel Megabytes" print "graph_category gunicorn" diff --git a/plugins/gunicorn/gunicorn_status b/plugins/gunicorn/gunicorn_status index 2c9fc888..78cd48d8 100755 --- a/plugins/gunicorn/gunicorn_status +++ b/plugins/gunicorn/gunicorn_status @@ -21,7 +21,7 @@ """ -import sys, os +import sys, os, re from subprocess import check_output from time import sleep @@ -87,7 +87,18 @@ class GunicornStatus(): return user_time + kernel_time def print_config(): - print "graph_title Gunicorn - Status" + instance = None + name = os.path.basename(sys.argv[0]) + if name != "gunicorn_status": + for r in ("^gunicorn_(.*?)_status$", "^gunicorn_status_(.*?)$"): + m = re.match(r, name, re.IGNORECASE) + if m: + instance = m.group(1) + break + graph_title = "graph_title Gunicorn - Status" + if instance: + graph_title = "%s - %s" % (graph_title, instance) + print graph_title print "graph_args -l 0" print "graph_vlabel Number of workers" print "graph_category gunicorn" diff --git a/plugins/hhvm/hhvm_ b/plugins/hhvm/hhvm_ index 42419999..e3db61be 100755 --- a/plugins/hhvm/hhvm_ +++ b/plugins/hhvm/hhvm_ @@ -40,7 +40,7 @@ server { =head2 APACHE 2.2 CONFIG FastCgiExternalServer /var/run/hhvm_admin.fcgi -host 127.0.0.1:8080 -Listen 127.0.0.1:8081 +Listen 127.0.0.1:8081 Alias /check-health /var/run/hhvm_admin.fcgi Alias /status.json /var/run/hhvm_admin.fcgi @@ -107,8 +107,6 @@ tc-profsize.label TC Profiling Size tc-profsize.info Translation Cache Profiling Size tc-coldsize.label TC Cold Size tc-coldsize.info Translation Cache Cold Size -tc-trampolinessize.label TC Trampoline Size -tc-trampolinessize.info Translation Cache Trampoline Size tc-frozensize.label TC Frozen Size tc-frozensize.info Translation Cache Frozen Size rds.label RDS Used Bytes @@ -121,7 +119,7 @@ EOF my $status = getJson( sprintf( '%s/status.json', $url ) ); printf( "multigraph hhvm_%s_threads\n", $graphName ); - printf( "threads.value %d\n", int( @{ $status->{'status'}->{'threads'} } ) ); + printf( "threads.value %d\n", int( @{ $status->{'status'}->{'threads'} } ) ); printf( "load.value %d\n", $health->{'load'} ); printf( "queued.value %d\n", $health->{'queued'} ); print "\n"; @@ -131,7 +129,6 @@ EOF printf( "tc-size.value %d\n", $health->{'tc-size'} ); printf( "tc-profsize.value %d\n", $health->{'tc-profsize'} ); printf( "tc-coldsize.value %d\n", $health->{'tc-coldsize'} ); - printf( "tc-trampolinessize.value %d\n", $health->{'tc-trampolinessize'} ); printf( "tc-frozensize.value %d\n", $health->{'tc-frozensize'} ); printf( "rds.value %d\n", $health->{'rds'} ); printf( "units.value %d\n", $health->{'units'} ); @@ -151,4 +148,4 @@ sub getJson( $ ) { die( sprintf( "Could not decode json from '%s'.", $url ) ); } return $data; -} \ No newline at end of file +} diff --git a/plugins/jenkins/jenkins_ b/plugins/jenkins/jenkins_ index 6112c8c2..7dd34068 100644 --- a/plugins/jenkins/jenkins_ +++ b/plugins/jenkins/jenkins_ @@ -66,8 +66,20 @@ my $wgetBin = "/usr/bin/wget"; my $type = basename($0); $type =~ s/jenkins_//; -my %states = ('blue' =>'stable', 'blue_anime' =>'stable', 'yellow'=>'unstable', 'yellow_anime'=>'unstable', 'red'=>'failing', 'red_anime'=>'failing', 'disabled'=>'disabled', 'notbuilt_anime' =>'disabled', 'aborted'=>'failing', 'aborted_anime'=>'failing' ); -my %counts = ('blue' => 0, 'yellow'=>0, 'red'=>0, 'disabled'=>0); +my %states = ( + 'blue' =>'stable', + 'blue_anime' =>'stable', + 'yellow'=>'unstable', + 'yellow_anime'=>'unstable', + 'red'=>'failing', + 'red_anime'=>'failing', + 'disabled'=>'disabled', + 'notbuilt' => 'disabled', + 'notbuilt_anime' =>'disabled', + 'aborted'=>'failing', + 'aborted_anime'=>'failing' +); +my %counts = ('stable' => 0, 'unstable'=>0, 'failing'=>0, 'disabled'=>0); if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { if( $type eq "results" ) { @@ -123,11 +135,15 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'jobs'}}) { - $counts{$cur->{'color'}} += 1; + if (defined $states{$cur->{'color'}}) { + $counts{$states{$cur->{'color'}}} += 1; + } else { + warn "Ignoring unknown color " . $cur->{'color'} . "\n" + } } foreach my $status (keys %counts) { - print "build_$states{$status}.value $counts{$status}\n"; + print "build_$status.value $counts{$status}\n"; } exit; } diff --git a/plugins/lxd/lxd_disk b/plugins/lxd/lxd_disk new file mode 100755 index 00000000..9e7c04f5 --- /dev/null +++ b/plugins/lxd/lxd_disk @@ -0,0 +1,48 @@ +#!/usr/bin/python3 + +import sys + +errors=[] + +HAS_LIB=True +try: + from pylxd import api +except: + HAS_LIB=False + errors.append("no pylxd module") + +c=None +HAS_ACCESS=True +try: + c=api.API() + c.container_list() +except: + HAS_ACCESS=False + errors.append("no socket access") + +if len(sys.argv) == 2 and sys.argv[1]=="autoconf": + if HAS_LIB and HAS_ACCESS: + print("yes") + else: + print("no ("+" and ".join(errors)+")") + sys.exit(0) + +if not (HAS_LIB and HAS_ACCESS): + # pylxd not installed or lxd socket not accessible + sys.exit(1) + +if len(sys.argv) == 2 and sys.argv[1]=="config": + print("graph_title LXD container disk usage") + print("graph_args --base 1000 --lower-limit 0") + print("graph_vlabel Bytes") + print("graph_category lxd") + print("graph_info This shows the disk usage of storage in containers. Make sure to install pylxd in python3.") + for name in c.container_list(): + for disk in c.container_info(name)['disk']: + print(name+"-"+disk+".label "+name) + print(name+"-"+disk+".draw LINE2") + sys.exit(0) + +for name in c.container_list(): + for disk in c.container_info(name)['disk']: + print(name+"-"+disk+".value "+str(c.container_info(name)['disk'][disk]['usage'])) diff --git a/plugins/lxd/lxd_mem b/plugins/lxd/lxd_mem new file mode 100755 index 00000000..6c797836 --- /dev/null +++ b/plugins/lxd/lxd_mem @@ -0,0 +1,46 @@ +#!/usr/bin/python3 + +import sys + +errors=[] + +HAS_LIB=True +try: + from pylxd import api +except: + HAS_LIB=False + errors.append("no pylxd module") + +c=None +HAS_ACCESS=True +try: + c=api.API() + c.container_list() +except: + HAS_ACCESS=False + errors.append("no socket access") + +if len(sys.argv) == 2 and sys.argv[1]=="autoconf": + if HAS_LIB and HAS_ACCESS: + print("yes") + else: + print("no ("+" and ".join(errors)+")") + sys.exit(0) + +if not (HAS_LIB and HAS_ACCESS): + # pylxd not installed or lxd socket not accessible + sys.exit(1) + +if len(sys.argv) == 2 and sys.argv[1]=="config": + print("graph_title LXD container memory") + print("graph_args --base 1024 --lower-limit 0") + print("graph_vlabel Bytes") + print("graph_category lxd") + print("graph_info This shows the memory usage of each container. Make sure to install pylxd in python3.") + for name in c.container_list(): + print(name+".label "+name) + print(name+".draw AREASTACK") + sys.exit(0) + +for name in c.container_list(): + print(name+".value "+str(c.container_info(name)['memory']['usage'])) diff --git a/plugins/mail/policyd-spf-python b/plugins/mail/policyd-spf-python new file mode 100755 index 00000000..f51a7b8b --- /dev/null +++ b/plugins/mail/policyd-spf-python @@ -0,0 +1,88 @@ +#! /bin/bash +# +# Munin plugin to monitor postfix-policyd-spf-python results +# Contributed by Alexander Koch +# +# This plugin is published under the terms of the MIT License. +# +# Parameters understood: +# config (required) +# autoconf (optional - used by munin-config) +# +# Config variables: +# logfile - Where to find the postfix log (mail.log) +# +# Add the following line to a file in /etc/munin/plugin-conf.d: +# env.logfile /var/log/your/mail.log +# +# Magic markers (optional - used by munin-config and installation scripts): +# +#%# family=auto +#%# capabilities=autoconf + + +# +# Configuration +# + +STAT_FILE=${STAT_FILE:-/var/lib/munin/plugin-state/plugin-plcyd-spf-python.state} +LOGFILE=${logfile:-/var/log/mail.log} + +if [ "$1" = "autoconf" ]; then + echo yes + exit 0 +fi + +if [ "$1" = "config" ]; then + echo 'graph_title SPF Check Results' + echo 'graph_category postfix' + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel Count/s' + + echo 'count_pass.label Pass' + echo 'count_pass.type DERIVE' + echo 'count_pass.min 0' + echo 'count_pass.colour 00cc00' + echo 'count_fail.label Fail' + echo 'count_fail.type DERIVE' + echo 'count_fail.min 0' + echo 'count_fail.colour cc0000' + echo 'count_none.label None' + echo 'count_none.type DERIVE' + echo 'count_none.min 0' + echo 'count_none.colour 0066b3' + echo 'count_temperror.label Temperror' + echo 'count_temperror.type DERIVE' + echo 'count_temperror.min 0' + echo 'count_temperror.colour ff8000' + echo 'count_neutral.label Neutral' + echo 'count_neutral.type DERIVE' + echo 'count_neutral.min 0' + echo 'count_neutral.colour ffcc00' + + exit 0 +fi + + +# +# Log parsing +# + +function get_log_count() { + egrep "policyd-spf\[[0-9]+\]: $1;" "$LOGFILE" | grep "$(date '+%b %e')" | wc -l +} + +PASS=$(get_log_count "Pass") +FAIL=$(get_log_count "Fail") +NONE=$(get_log_count "None") +TEMPERR=$(get_log_count "Temperror") +NEUTRAL=$(get_log_count "Neutral") + +echo "count_pass.value $PASS" +echo "count_fail.value $FAIL" +echo "count_none.value $NONE" +echo "count_temperror.value $TEMPERR" +echo "count_neutral.value $NEUTRAL" + + +exit 0 diff --git a/plugins/memcached_ext/memcached_ext_bytes_ b/plugins/memcached_ext/memcached_ext_bytes_ new file mode 100755 index 00000000..10642a94 --- /dev/null +++ b/plugins/memcached_ext/memcached_ext_bytes_ @@ -0,0 +1,57 @@ +#!/usr/bin/env perl +# ex:ts=4 + +use strict; +use warnings; + +use Cache::Memcached; + +# Based on original plugin, extended to unix socket use +# https://github.com/western, westroads@gmail.com + +=head1 example config for /plugin-conf.d/munin-node + +[memcached_bytes_1] +env.server 127.0.0.1:11211 +env.label "first local server" + +[memcached_bytes_2] +env.server /var/run/memcached/memcached.sock +env.label "second local server" + +=cut + +my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; +unless( $label ){ + + if( $0 =~ /memcached_ext_bytes_([\d\w]+)$/ ){ + $label = $1; + } +} + +my $cmd = shift || ''; +if ($cmd eq 'config') { + print "graph_title Memcached bytes used on $label\n"; + print "graph_args --base 1024 -l 0\n"; + print "graph_vlabel bytes\n"; + print "graph_category memcached\n"; + print "graph_info This graph monitors the size of the memcached cache.\n"; + print "bytes.label bytes used\n"; + print "bytes.info Number of bytes currently used\n"; + print "bytes.min 0\n"; + print "bytes.draw AREA\n"; + print "maxbytes.label maximum available\n"; + print "maxbytes.info The configured cache size\n"; + print "maxbytes.min 0\n"; + exit 0; +} + + +my $server = exists $ENV{'server'} ? $ENV{'server'} : '127.0.0.1:11211'; + +my $memd = new Cache::Memcached { 'servers' => [$server] }; +my $memstats = $memd->stats(['misc']); + +print "bytes.value " . $memstats->{hosts}->{$server}->{misc}->{bytes} . "\n"; +print "maxbytes.value " . + $memstats->{hosts}->{$server}->{misc}->{limit_maxbytes} . "\n"; diff --git a/plugins/memcached_ext/memcached_ext_connections_ b/plugins/memcached_ext/memcached_ext_connections_ new file mode 100755 index 00000000..57d1be7c --- /dev/null +++ b/plugins/memcached_ext/memcached_ext_connections_ @@ -0,0 +1,53 @@ +#!/usr/bin/env perl +# ex:ts=4 + +use strict; +use warnings; + +use Cache::Memcached; + +# Based on original plugin, extended to unix socket use +# https://github.com/western, westroads@gmail.com + +=head1 example config for /plugin-conf.d/munin-node + +[memcached_connections_1] +env.server 127.0.0.1:11211 +env.label "first local server" + +[memcached_connections_2] +env.server /var/run/memcached/memcached.sock +env.label "second local server" + +=cut + +my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; +unless( $label ){ + + if( $0 =~ /memcached_ext_connections_([\w\d]+)$/ ){ + $label = $1; + } +} + +my $cmd = shift || ''; +if ($cmd eq 'config') { + print "graph_title Memcached connections on $label\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel connections\n"; + print "graph_category memcached\n"; + print "graph_info This graph monitors the connections to the memcached server.\n"; + print "connections.label connections\n"; + print "connections.info Number of connections to memcached\n"; + print "connections.min 0\n"; + print "connections.draw AREA\n"; + exit 0; +} + + +my $server = exists $ENV{'server'} ? $ENV{'server'} : '127.0.0.1:11211'; + +my $memd = new Cache::Memcached { 'servers' => [$server] }; +my $memstats = $memd->stats(['misc']); + +print "connections.value " . + $memstats->{hosts}->{$server}->{misc}->{curr_connections} . "\n"; diff --git a/plugins/memcached_ext/memcached_ext_hits_ b/plugins/memcached_ext/memcached_ext_hits_ new file mode 100755 index 00000000..d2a7724d --- /dev/null +++ b/plugins/memcached_ext/memcached_ext_hits_ @@ -0,0 +1,60 @@ +#!/usr/bin/env perl +# ex:ts=4 + +use strict; +use warnings; + +use Cache::Memcached; + +# Based on original plugin, extended to unix socket use +# https://github.com/western, westroads@gmail.com + +=head1 example config for /plugin-conf.d/munin-node + +[memcached_hits_1] +env.server 127.0.0.1:11211 +env.label "first local server" + +[memcached_hits_2] +env.server /var/run/memcached/memcached.sock +env.label "second local server" + +=cut + +my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; +unless( $label ){ + + if( $0 =~ /memcached_ext_hits_([\w\d]+)$/ ){ + $label = $1; + } +} + + + +my $cmd = shift || ''; +if ($cmd eq 'config') { + print "graph_title Memcached cache hits and misses on $label\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel requests\n"; + print "graph_category memcached\n"; + print "graph_info This graph monitors the number of cache hits and misses.\n"; + print "hits.label hits\n"; + print "hits.info Number of cache hits\n"; + print "hits.min 0\n"; + print "hits.type DERIVE\n"; + print "misses.label misses\n"; + print "misses.info Number of cache misses\n"; + print "misses.min 0\n"; + print "misses.type DERIVE\n"; + print "misses.draw AREA\n"; + exit 0; +} + +my $server = exists $ENV{'server'} ? $ENV{'server'} : '127.0.0.1:11211'; + +my $memd = new Cache::Memcached { 'servers' => [$server] }; +my $memstats = $memd->stats(['misc']); + +print "hits.value " . $memstats->{hosts}->{$server}->{misc}->{get_hits} . "\n"; +print "misses.value " . + $memstats->{hosts}->{$server}->{misc}->{get_misses} . "\n"; diff --git a/plugins/memcached_ext/memcached_ext_items_ b/plugins/memcached_ext/memcached_ext_items_ new file mode 100755 index 00000000..39b56503 --- /dev/null +++ b/plugins/memcached_ext/memcached_ext_items_ @@ -0,0 +1,53 @@ +#!/usr/bin/env perl +# ex:ts=4 + +use strict; +use warnings; + +use Cache::Memcached; + +# Based on original plugin, extended to unix socket use +# https://github.com/western, westroads@gmail.com + +=head1 example config for /plugin-conf.d/munin-node + +[memcached_items_1] +env.server 127.0.0.1:11211 +env.label "first local server" + +[memcached_items_2] +env.server /var/run/memcached/memcached.sock +env.label "second local server" + +=cut + +my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; +unless( $label ){ + + if( $0 =~ /memcached_ext_items_([\w\d]+)$/ ){ + $label = $1; + } +} + + +my $cmd = shift || ''; +if ($cmd eq 'config') { + print "graph_title Memcached cached items on $label\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel items\n"; + print "graph_category memcached\n"; + print "graph_info This graph monitors the number of items stored by the memcached server.\n"; + print "items.label items\n"; + print "items.info Number of cached items\n"; + print "items.min 0\n"; + print "items.draw AREA\n"; + exit 0; +} + +my $server = exists $ENV{'server'} ? $ENV{'server'} : '127.0.0.1:11211'; + +my $memd = new Cache::Memcached { 'servers' => [$server] }; +my $memstats = $memd->stats(['misc']); + +print "items.value " . + $memstats->{hosts}->{$server}->{misc}->{curr_items} . "\n"; diff --git a/plugins/memcached_ext/memcached_ext_requests_ b/plugins/memcached_ext/memcached_ext_requests_ new file mode 100755 index 00000000..b7b75fce --- /dev/null +++ b/plugins/memcached_ext/memcached_ext_requests_ @@ -0,0 +1,59 @@ +#!/usr/bin/env perl +# ex:ts=4 + +use strict; +use warnings; + +use Cache::Memcached; + +# Based on original plugin, extended to unix socket use +# https://github.com/western, westroads@gmail.com + +=head1 example config for /plugin-conf.d/munin-node + +[memcached_requests_1] +env.server 127.0.0.1:11211 +env.label "first local server" + +[memcached_requests_2] +env.server /var/run/memcached/memcached.sock +env.label "second local server" + +=cut + +my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; +unless( $label ){ + + if( $0 =~ /memcached_ext_requests_([\w\d]+)$/ ){ + $label = $1; + } +} + + +my $cmd = shift || ''; +if ($cmd eq 'config') { + print "graph_title Memcached requests on $label\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel requests\n"; + print "graph_category memcached\n"; + print "graph_info This graph monitors the number of get and set requests.\n"; + print "gets.label gets\n"; + print "gets.info Number of get requests\n"; + print "gets.min 0\n"; + print "gets.type DERIVE\n"; + print "gets.draw AREA\n"; + print "sets.label sets\n"; + print "sets.info Number of set requests\n"; + print "sets.min 0\n"; + print "sets.type DERIVE\n"; + print "sets.draw STACK\n"; + exit 0; +} + +my $server = exists $ENV{'server'} ? $ENV{'server'} : '127.0.0.1:11211'; + +my $memd = new Cache::Memcached { 'servers' => [$server] }; +my $memstats = $memd->stats(['misc']); + +print "gets.value " . $memstats->{hosts}->{$server}->{misc}->{cmd_get} . "\n"; +print "sets.value " . $memstats->{hosts}->{$server}->{misc}->{cmd_set} . "\n"; diff --git a/plugins/memcached_ext/memcached_ext_traffic_ b/plugins/memcached_ext/memcached_ext_traffic_ new file mode 100755 index 00000000..640efc43 --- /dev/null +++ b/plugins/memcached_ext/memcached_ext_traffic_ @@ -0,0 +1,60 @@ +#!/usr/bin/env perl +# ex:ts=4 + +use strict; +use warnings; + +use Cache::Memcached; + +# Based on original plugin, extended to unix socket use +# https://github.com/western, westroads@gmail.com + +=head1 example config for /plugin-conf.d/munin-node + +[memcached_traffic_1] +env.server 127.0.0.1:11211 +env.label "first local server" + +[memcached_traffic_2] +env.server /var/run/memcached/memcached.sock +env.label "second local server" + +=cut + +my $label = exists $ENV{'label'} ? $ENV{'label'} : ''; +unless( $label ){ + + if( $0 =~ /memcached_ext_traffic_([\w\d]+)$/ ){ + $label = $1; + } +} + + +my $cmd = shift || ''; +if ($cmd eq 'config') { + print "graph_title Memcached network traffic on $label\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel bits per \${graph_period}\n"; + print "graph_category memcached\n"; + print "graph_info This graph monitors the network traffic of the memcached server.\n"; + print "up.label bits in\n"; + print "up.info Traffic received by memcached\n"; + print "up.min 0\n"; + print "up.cdef up,8,*\n"; + print "up.type COUNTER\n"; + print "up.draw AREA\n"; + print "down.label bits out\n"; + print "down.info Traffic sent by memcached\n"; + print "down.min 0\n"; + print "down.cdef down,8,*\n"; + print "down.type COUNTER\n"; + exit 0; +} + +my $server = exists $ENV{'server'} ? $ENV{'server'} : '127.0.0.1:11211'; + +my $memd = new Cache::Memcached { 'servers' => [$server] }; +my $memstats = $memd->stats(['misc']); + +print "up.value " . $memstats->{hosts}->{$server}->{misc}->{bytes_read} . "\n"; +print "down.value " . $memstats->{hosts}->{$server}->{misc}->{bytes_written} . "\n"; diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index 5d34a3a3..f89bd4db 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -54,6 +54,8 @@ if len(sys.argv) > 1 and sys.argv[1] == 'config': for process in procs: for stat in procs[process]: print "monit_%s_%s.label %s.%s" % (process, stat, process, stat) + if stat == 'total_memory': + print "monit_%s_%s.warning 1:" % (process, stat) sys.exit(0) for process in procs: diff --git a/plugins/moodle/moodle_files.php b/plugins/moodle/moodle_files.php index cdf6c978..13a9a31e 100644 --- a/plugins/moodle/moodle_files.php +++ b/plugins/moodle/moodle_files.php @@ -81,7 +81,6 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_args --base 1024 --lower-limit 0\n"; echo "graph_vlabel size\n"; echo "graph_category Moodle\n"; - echo "graph_scale no\n"; echo "graph_total total\n"; echo "graph_info Displays the total size of moodle users files and repartition by type\n"; @@ -119,7 +118,6 @@ echo "graph_title Moodle Files Size\n"; echo "graph_args --base 1024 --lower-limit 0\n"; echo "graph_vlabel size\n"; echo "graph_category Moodle\n"; -echo "graph_scale yes\n"; echo "graph_total total\n"; echo "graph_info Displays the total size of moodle users files and repartition by type\n"; diff --git a/plugins/network/arp_bsd_ b/plugins/network/arp_bsd_ new file mode 100644 index 00000000..7572e8b6 --- /dev/null +++ b/plugins/network/arp_bsd_ @@ -0,0 +1,54 @@ +#!/bin/sh +# +# Wildcard-plugin to monitor arp on interfaces. To monitor an +# interface, link arp_bsd_ to this file. E.g. +# +# ln -s /usr/local/share/munin/plugins/arp_bsd_ /usr/local/etc/munin/plugins/arp_bsd_vlanX +# +# ...will monitor arp on interface vlanX. +# +# Any device found in /sbin/ifconfig can be monitored. +# +# Bugs : This plugins has been tested extensively on FreeBSD only +# +# Author : Luc Duchosal +# +# Magic markers (optional - used by munin-config and some installation +# scripts): +# +#%# family=auto +#%# capabilities=autoconf suggest + + +INTERFACE=`basename $0 | sed 's/^arp_bsd_//g'` + +if [ "$1" = "autoconf" ]; then + if [ -x /sbin/ifconfig ]; then + echo yes + exit 0 + else + echo "no (/sbin/ifconfig not found)" + exit 0 + fi +fi + +if [ "$1" = "suggest" ]; then + if [ -x /sbin/ifconfig ]; then + /sbin/ifconfig -a | /usr/bin/grep -E '^[a-z]' | /usr/bin/awk -F\: '{print $1;}' + exit 0 + else + exit 1 + fi +fi + +if [ "$1" = "config" ]; then + + echo "graph_title $INTERFACE arp" + echo 'graph_args --base 1000' + echo 'graph_vlabel arp per ${graph_period}' + echo 'graph_category network' + echo 'arp.label arp' + exit 0 +fi + +/usr/sbin/arp -an | /usr/bin/grep $INTERFACE | /usr/bin/wc -l | /usr/bin/awk '{ print "arp.value", $1;}' diff --git a/plugins/network/if1sec_ b/plugins/network/if1sec_ index 28b45ec7..1414fa3b 100755 --- a/plugins/network/if1sec_ +++ b/plugins/network/if1sec_ @@ -1,15 +1,11 @@ #! /bin/sh -# Currently the plugin does *not* autostart -# -# It has to be launched via rc.d : -# munin-run if1sec_eth0 acquire -pluginfull="$0" # full name of plugin -plugin="${0##*/}" # name of plugin +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 +IFACE="${0##*/if1sec_}" # interface if [ ! -r "/sys/class/net/$IFACE/statistics/tx_bytes" ] then @@ -19,44 +15,86 @@ 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 + ( + exec <&- >&- 2>&- + while sleep 1 + do + read tx < /sys/class/net/$IFACE/statistics/tx_bytes + read rx < /sys/class/net/$IFACE/statistics/rx_bytes + echo "$tx $rx" + done | awk "{ + now = systime() + print \"${IFACE}_tx.value \" now \":\" \$1 + print \"${IFACE}_rx.value \" now \":\" \$2 + system(\"\") + }" >> $cache + ) & + echo $! > $pidfile + exit 0 fi if [ "$1" = "config" ] then - cat < ${cache} diff --git a/plugins/network/pf_tables_ b/plugins/network/pf_tables_ new file mode 100644 index 00000000..3e98903f --- /dev/null +++ b/plugins/network/pf_tables_ @@ -0,0 +1,252 @@ +#!/usr/bin/perl -w +# -*- perl -*- + +=head1 NAME + +pf_tables : Munin plugin to monitor pf tables. +Inout: bandwidth usage for table +Addresses: number of entries in table + + +=head1 APPLICABLE SYSTEMS + +Should work on any BSD that has pf(4). + +Examples: + +=over + +=item pf_tables_inout_tablename + +=item pf_tables_addresses_authenticated + +=item pf_tables_addresses_badboys + + +=head1 CONFIGURATION + +[pf_tables_*] +user root + +=head1 INTERPRETATION + +The plugin simply runs the pfctl -sTables -vvv command and counts the number of +Addresses and InBytes/OutBytes in each table. + +=head1 BUGS + +Only tested extensively on FreeBSD. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 VERSION + + $Id$ + +=head1 AUTHOR + +Copyright (C) 2015. + +Original version by Luc Duchosal (at) arcantel (dot) ch. +Created by Luc Duchosal, 2015 + +=head1 LICENSE + +BSD + +=cut + + +use strict; +use Munin::Plugin; + +$0 =~ /pf_tables_(addresses|inout)_(.+)$/; +my $name = $2; +my $operation = $1; + +if ( defined($ARGV[0])) { + if ($ARGV[0] eq 'autoconf') { + print "yes\n"; + exit 0; + } + + if ($ARGV[0] eq "config") { + + if (!defined($name)) { + print "Unknown table\n"; + exit 0; + } + + if (!defined($operation)) { + print "Unknown operation\n"; + exit 0; + } + + if ($operation =~ m/addresses/) { + + print "graph_title Connected users ($name)\n"; + print "graph_args --base 1000 -l 0\n"; + print "graph_vlabel Users\n"; + print "graph_scale no\n"; + print "graph_category pf\n"; + print "graph_printf %3.0lf\n"; + + print "users.label users\n"; + print "users.draw AREASTACK\n"; + print "users.colour 00C000\n"; + foreach my $field (qw(users)) { + print_thresholds($field); + } + } + + if ($operation =~ m/inout/) { + + print "graph_title Network bandwidth ($name)\n"; + print "graph_args --base 1024 -l 0\n"; + print "graph_vlabel Bandwidth\n"; + print "graph_scale yes\n"; + print "graph_category pf\n"; +# print "graph_printf %3.0lf\n"; + + print "in.label in\n"; + print "in.type DERIVE\n"; + print "in.draw AREASTACK\n"; + print "in.colour C00000\n"; + print "in.cdef in,8,*\n"; + print "in.min 0\n"; + print "in.graph no\n"; + print "out.label out\n"; + print "out.type DERIVE\n"; + print "out.negative in\n"; + print "out.draw AREASTACK\n"; + print "out.colour 0000C0\n"; + print "out.cdef out,8,*\n"; + print "out.min 0\n"; + print "out.graph no\n"; + + foreach my $field (qw(in out)) { + print_thresholds($field); + } + + } + exit 0; + } + + if ($ARGV[0] eq "suggest") { + my %tables = &tables(); + foreach my $key (keys(%tables)) { + print "addresses_$key\n"; + print "inout_$key\n"; + } + exit 0; + } + +} + +if (!defined($name)) { + print "Usage: pf_tables_addresses_tablename or pf_tables_inout_tablename\n"; + exit 1; +} + +my %tables = &tables(); +if (!exists $tables{$name}) { + print "Unknown table name $name\n"; + exit 2; +} + +if ($operation =~ m/addresses/) { + my $users = $tables{$name}->{"addresses"}; + print "users.value $users\n"; +} + +if ($operation =~ m/inout/) { + my $in = $tables{$name}->{"inpassbytes"}; + my $out = $tables{$name}->{"outpassbytes"}; + print "in.value $in\n"; + print "out.value $out\n"; +} + + +sub tables { + + # # pfctl -s Tables -vv + # -pa-r-- auth + # Addresses: 0 + # Cleared: Fri Sep 18 17:34:42 2015 + # References: [ Anchors: 0 Rules: 14 ] + # Evaluations: [ NoMatch: 43624 Match: 788 ] + # In/Block: [ Packets: 0 Bytes: 0 ] + # In/Pass: [ Packets: 30908 Bytes: 2704516 ] + # In/XPass: [ Packets: 124 Bytes: 7897 ] + # Out/Block: [ Packets: 0 Bytes: 0 ] + # Out/Pass: [ Packets: 30288 Bytes: 26313114 ] + # Out/XPass: [ Packets: 89 Bytes: 21166 ] + + my $output = `/sbin/pfctl -s Tables -vv 2> /dev/null`; + my %tables; + my $name; + + foreach (split(/\n/, $output)) { + + if (m|^[cpairhC\-]{7}\s+(\w+)$|) { + $name = $1; + $tables{$name}->{"name"} = $1; + next; + } + + if (m|Addresses:\s+([0-9]+)$|) { + $tables{$name}->{"addresses"} = $1; + next; + } + + if (m|Cleared:\s+(.+)$|) { + $tables{$name}->{"cleared"} = $1; + next; + } + + if (m|In/Block:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) { + $tables{$name}->{"inblockpackets"} = $1; + $tables{$name}->{"inblockbytes"} = $2; + next; + } + + if (m|In/Pass:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) { + $tables{$name}->{"inpasspackets"} = $1; + $tables{$name}->{"inpassbytes"} = $2; + next; + } + + if (m|In/XPass:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) { + $tables{$name}->{"inxpasspackets"} = $1; + $tables{$name}->{"inxpassbytes"} = $2; + next; + } + + if (m|Out/Block:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) { + $tables{$name}->{"outblockpackets"} = $1; + $tables{$name}->{"outblockbytes"} = $2; + next; + } + + if (m|Out/Pass:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) { + $tables{$name}->{"outpasspackets"} = $1; + $tables{$name}->{"outpassbytes"} = $2; + next; + } + + if (m|Out/XPass:\s+\[\s+Packets:\s+([0-9]+)\s+Bytes:\s+([0-9]+)\s+\]$|) { + $tables{$name}->{"outxpasspackets"} = $1; + $tables{$name}->{"outxpassbytes"} = $2; + next; + } + + } + + return %tables; + +} + +# vim:syntax=perl diff --git a/plugins/network/radio b/plugins/network/radio index bd06bed6..1c815823 100755 --- a/plugins/network/radio +++ b/plugins/network/radio @@ -1,155 +1,140 @@ #!/usr/bin/php "ice", // server-type (ice/shout) - "host" => "192.168.1.5", // server hostname or ip - "port" => 8000, // server port - "mountpoint" => "eclectix.mp3", // mountpoint to check (icecast only) - "name" => "IceCast" // name for use in munin graphs - ), - // SERVER #2 - array( "type" => "shout", // server-type - "host" => "radio.eclectix.de", // server hostname or ip - "port" => 8000, // server port - "name" => "ShoutCast" // name for use in munin graphs - ) - ); - -// -------------- CONFIGURATION END ---------------------------------------------------------------- - - function getIce( $host, $port, $mount, $name ) { - $error = false; - $fp = fsockopen( $host, $port, $errno, $errstr, 10 ); - if ( !$fp ) { - $error = $errstr ."(". $errno .")"; - } else { - fputs( $fp, "GET /status HTTP/1.1\r\n" ); - fputs( $fp, "Host: ". $host ."\r\n" ); - fputs($fp, "User-Agent: Mozilla\r\n"); - fputs( $fp, "Connection: close\r\n\r\n" ); - - $xml = ""; - - while ( !feof( $fp ) ) { - $xml .= fgets( $fp, 512 ); - } - - fclose($fp); - - if ( stristr( $xml, "HTTP/1.0 200 OK" ) == true ) { - $xml = trim( substr( $xml, 42 ) ); - } else { - $error = "Bad login"; - } - if ( !$error ) { - $res = array( "found" => true ); - - $mount = str_replace( ".", "\.", $mount ); - preg_match_all( "/Mount Point : \(\/(". $mount .")\).*?\\Current Listeners:\<\/td\>\(\d*?)\<\/td\>\<\/tr\>\Peak Listeners:\<\/td\>\(\d*?)\<\/td\>\<\/tr\>/s", $xml, $parser ); - - $res["mount"] = $parser[1][0]; - $res["listeners"] = $parser[2][0]; - $res["listeners_peak"] = $parser[3][0]; - $res["name"] = $name; - } else { - $res = $error; - } - } - return $res; - } - - function getShout( $host, $port, $name ) { - $error = false; - $fp = fsockopen( $host, $port, $errno, $errstr, 10 ); - if ( !$fp ) { - $error = $errstr ."(". $errno .")"; - } else { - fputs( $fp, "GET / HTTP/1.0\r\n" ); - fputs($fp, "User-Agent: Mozilla\r\n"); - fputs( $fp, "Connection: close\r\n\r\n" ); - - $xml = ""; - - while ( !feof( $fp ) ) { - $xml .= fgets($fp, 512); - } - fclose( $fp ); - - if ( stristr( $xml, "HTTP/1.0 200 OK" ) == true ) { - $xml = trim( substr( $xml, 42 ) ); - } else { - $error = "Bad login"; - } - if ( !$error ) { - $res = array( "found" => true ); - - preg_match_all( "/.*?Stream Status: \<\/font\>\<\/td\>\\\Stream is up at \d*? kbps with \(\d*?) of \d*? listeners \(\d*? unique\)\<\/b\>\<\/b\>/s", $xml, $parser ); - - $res["listeners"] = $parser[1][0]; - $res["name"] = $name; - - } else { - $res = $error; - } - } - return $res; +// -------------- CONFIGURATION START --------------------------------------- + $cfg = array( + // SERVER #1 + array( "name" => "IceCast", // name for munin + "type" => "ice", // server-type (ice/shout) + "host" => "ice.example.com", // server hostname or ip + "port" => 8000, // server port + "mountpoint" => "live" // mountpoint to check + // (icecast only) + ), + // SERVER #2 + array( "name" => "ShoutCast", // name for munin + "type" => "shout", // server-type + "host" => "127.0.0.1", // server hostname or ip + "port" => 8000 // server port + ) + ); +// -------------- CONFIGURATION END ----------------------------------------- + error_reporting(E_ERROR); + function getIce( $host, $port, $mount, $name ) { + $error = false; + if ( !$fp = fsockopen( $host, $port, $errno, $errstr, 10 ) ) { + $error = $errstr ."(". $errno .")"; + } else { + fputs( $fp, "GET /status.xsl HTTP/1.1\r\n" ); + fputs( $fp, "Host: ". $host ."\r\n" ); + fputs( $fp, "User-Agent: Mozilla\r\n" ); + fputs( $fp, "Connection: close\r\n\r\n" ); + $xml = ""; + while ( !feof( $fp ) ) { + $xml .= fgets( $fp, 512 ); + } + fclose( $fp ); + if ( stristr( $xml, "HTTP/1.0 200 OK" ) == true ) { + $xml = trim( substr( $xml, 42 ) ); + } else { + $error = "Bad login"; + } + if ( !$error ) { + $res = array( "found" => true ); + $mount = str_replace( ".", "\.", $mount ); + preg_match_all( "/Mount Point \/(". $mount .").*?\\Current Listeners:\<\/td\>\(\d*?)\<\/td\>\<\/tr\>\Peak Listeners:\<\/td\>\(\d*?)\<\/td\>\<\/tr\>/s", $xml, $parser ); + $res["mount"] = $parser[1][0]; + $res["listeners"] = intval( $parser[2][0] ); + $res["listeners_peak"] = intval( $parser[3][0] ); + $res["name"] = $name; + } else { + $res = $error; + } + } + return $res; + } + function getShout( $host, $port, $name ) { + $error = false; + if ( !$fp = fsockopen( $host, $port, $errno, $errstr, 10 ) ) { + $error = $errstr ."(". $errno .")"; + } else { + fputs( $fp, "GET /index.html?sid=1 HTTP/1.0\r\n" ); + fputs( $fp, "User-Agent: Mozilla\r\n" ); + fputs( $fp, "Connection: close\r\n\r\n" ); + $xml = ""; + while ( !feof( $fp ) ) { + $xml .= fgets( $fp, 512 ); + } + fclose( $fp ); + if ( stristr( $xml, "HTTP/1.1 200 OK" ) == true ) { + $xml = trim( substr( $xml, 42 ) ); + } else { + $error = "Bad login"; + } + if ( !$error ) { + $res = array( "found" => true ); + preg_match_all( "/.*?Stream Status: <\/td\>\Stream is up at \d*? kbps with (\d*?) of \d*? listeners/s", $xml, $parser ); + $res["listeners"] = intval( $parser[1][0] ); + $res["name"] = $name; + } else { + $res = $error; + } + } + return $res; + } + if ( !isset( $argv[1] ) ) $argv[1] = ''; + switch( $argv[1] ) { + case "config": + echo "graph_title Stream Listeners\n"; + echo "graph_category Network\n"; + echo "graph_vlabel listeners\n"; + echo "graph_hlabel listeners\n"; + echo "graph_args --base 1000 -l 0\n"; + echo "graph_scale no\n"; + echo "graph_info Number of listeners to shout- and / or icecast streams\n"; + echo "complete.info Complete listeners\n"; + echo "complete.label complete\n"; + echo "graph_order"; + foreach ( $cfg as $c ) { + echo " ". strtolower( $c["name"] ); + } + echo " complete\n"; + foreach ( $cfg as $c ) { + echo strtolower( $c["name"] ) .".info ". $c["name"] ." listeners\n"; + echo strtolower( $c["name"] ) .".label ". strtolower( $c["name"] ) ."\n"; + } + break; + default: + $complete = 0; + foreach ( $cfg as $c ) { + switch ( $c["type"] ) { + case "ice": + $res = getIce( $c["host"], $c["port"], $c["mountpoint"], $c["name"] ); + break; + case "shout": + $res = getShout( $c["host"], $c["port"], $c["name"] ); + break; } - - switch( $argv[1] ) { - case "config": - echo "graph_title Stream Listeners\n"; - echo "graph_category Network\n"; - echo "graph_vlabel listeners\n"; - echo "graph_hlabel listeners\n"; - echo "graph_args --base 1000 -l 0\n"; - echo "graph_scale no\n"; - echo "graph_order"; - foreach ( $cfg as $c ) { - echo " ". strtolower( $c["name"] ); - } - echo " complete\n"; -// echo "\n"; - echo "graph_info Number of listeners to shout- and / or icecast streams\n"; - foreach ( $cfg as $c ) { - echo strtolower( $c["name"] ) .".info ". $c["name"] ." listeners\n"; - echo strtolower( $c["name"] ) .".label ". strtolower( $c["name"] ) ."\n"; - } - echo "complete.info Complete listeners\n"; - echo "complete.label complete\n"; - break; - default: - $complete = 0; - - foreach ( $cfg as $c ) { - switch ( $c["type"] ) { - case "ice": - $res = getIce( $c["host"], $c["port"], $c["mountpoint"], $c["name"] ); - $complete += $res["listeners"]; - break; - case "shout": - $res = getShout( $c["host"], $c["port"], $c["name"] ); - $complete += $res["listeners"]; - break; - } - echo strtolower($c["name"]) .".value ". $res["listeners"] ."\n"; - } - - echo "complete.value ". $complete ."\n"; - break; - } + if ( is_array( $res ) ) { + echo strtolower($c["name"]) .".value ". $res["listeners"] ."\n"; + $complete += $res["listeners"]; + } else { + echo strtolower($c["name"]) .".value 0\n"; + } + } + echo "complete.value ". $complete ."\n"; + break; + } ?> diff --git a/plugins/nginx/nginx_upstream_multi_ b/plugins/nginx/nginx_upstream_multi_ index 07780041..d22fc80a 100755 --- a/plugins/nginx/nginx_upstream_multi_ +++ b/plugins/nginx/nginx_upstream_multi_ @@ -25,9 +25,12 @@ # Use it in your site configuration (/etc/nginx/sites-enabled/anything.conf): # access_log /var/log/nginx/upstream.log upstream; # +# Attention! Because munin-node does not have read permission for nginx log files we need to run it as root. +# # And specify some options in /etc/munin/plugin-conf.d/munin-node: # # [nginx_upstream_multi_upstream] +# user root # env.graphs cache http time request # env.log /var/log/nginx/upstream.log # env.upstream 10.0.0.1:80 10.0.0.2:8080 unix:/tmp/upstream3 @@ -199,8 +202,8 @@ else: try: logHandle = open(logPath, "r") - except Exception: - print "Log file %s not readable" % logPath + except Exception as e: + print "Log file %s not readable: %s" % (logPath, e.strerror) sys.exit(1) try: @@ -278,7 +281,8 @@ else: lastByteHandle = open(lastBytePath, "w") lastByteHandle.write(str(logHandle.tell())) lastByteHandle.close() - except Exception: + except Exception as e: + print e.strerror sys.exit(1) logHandle.close() diff --git a/plugins/openntpd/openntp_offset b/plugins/openntpd/openntp_offset new file mode 100755 index 00000000..dbd2c1a4 --- /dev/null +++ b/plugins/openntpd/openntp_offset @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Plugin to measure OpenNTPd offset, delay and jitter for the active peer. +# +# Usage: Place it in the munin plugins directory (/usr/local/etc/munin/plugins/ +# or /etc/munin/plugins/). Might require running as root in order to access the +# ntpctl socket. +# +# AUTHOR: Vladimir Krstulja +# LICENSE: GPLv2 +# +#%# family=auto +#%# capabilities=autoconf + +NTPCTL=`which ntpctl` + +# Config +if [ "$1" = "autoconf" ] ; then + if [ -f "$NTPCTL" ]; then + echo 'yes' + else + echo 'no (no ntpctl)' + fi + exit 0 +fi + +# Autoconf +if [ "$1" = "config" ] ; then + echo "graph_title OpenNTP offset statistics for active peer" + echo "graph_args --base 1000 --vertical-label seconds --lower-limit 0" + echo "graph_category time" + echo "graph_info Current status: `$NTPCTL -s status`" + echo "delay.label Delay" + echo "delay.cdef delay,1000,/" + echo "offset.label Offset" + echo "offset.cdef offset,1000,/" + echo "jitter.label Jitter" + echo "jitter.cdef jitter,1000,/" + exit 0 +fi + +# Main plugin function +$NTPCTL -s all | awk '/\*/{printf("offset.value %.3f\ndelay.value %.3f\njitter.value %.3f\n",$7,$8,$9)}' + diff --git a/plugins/php/php_errors b/plugins/php/php_errors deleted file mode 100644 index b38ce74c..00000000 --- a/plugins/php/php_errors +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh -# -# Plugin to monitor error.log from apache server. -# Revision 0.1 2011/06/17 12:00:00 Ulrich Lusseau -# Initial revision -# -# Parameters: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# Magick markers (optional): -#%# family=auto -#%# capabilities=autoconf -# config example for /etc/munin/plugin-conf.d/munin-node -#[apache_log] -#user root -#env.logfile /home/newsite/logs/errors.log -# - - -LOG=${logfile:-/var/log/apache2/error.log} - - -if [ "$1" = "autoconf" ]; then - if [ -r "$LOG" ]; then - echo yes - exit 0 - else - echo no - exit 1 - fi -fi - -if [ "$1" = "config" ]; then - - echo 'graph_title PHP Errors from ' $LOG - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel Errors' - echo 'LogWarning.label PHP Warning errors' - echo 'LogNotice.label PHP Notice errors' - echo 'LogFatal.label PHP Fatal errors' - echo 'LogFile.label File does not exist errors' - exit 0 -fi - -awk 'BEGIN{c["LogWarning"]=0;c["LogNotice"]=0;c["LogFatal"]=0;c["LogFile"]=0; } - /PHP Warning/{c["LogWarning"]++} - /PHP Notice/{c["LogNotice"]++} - /PHP Fatal error/{c["LogFatal"]++} - /File does not exist/{c["LogFile"]++} - END{for(i in c){print i".value " c[i]} }' < $LOG diff --git a/plugins/php/php_errors_ b/plugins/php/php_errors_ new file mode 100644 index 00000000..e8788368 --- /dev/null +++ b/plugins/php/php_errors_ @@ -0,0 +1,66 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + +Plugin to monitor error.log from apache server + +=head1 CONFIGURATION + +[php_errors_newsite] + user www-data + env.logfile /home/newsite/logs/errors.log /var/log/php/otherlog.log + +=head1 AUTHOR + +Raphaël Droz + +Revision 0.2 2016/03/23 22:00:00 Raphaël Droz +Revision 0.1 2011/06/17 12:00:00 Ulrich Lusseau + +=head1 MAGICK MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + + +. $MUNIN_LIBDIR/plugins/plugin.sh + +LOGS=${logfile:-/var/log/apache2/error.log} + + +if [[ $1 == autoconf ]]; then + for LOG in $LOGS; do + if [[ ! -r $LOGS ]]; then + echo no + exit 1 + fi + done + + echo yes + exit 0 +fi + +if [[ $1 == config ]]; then + echo 'graph_title PHP Errors from ' $LOGS + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel Errors' + echo 'LogWarning.label PHP Warning errors' + echo 'LogNotice.label PHP Notice errors' + echo 'LogFatal.label PHP Fatal errors' + echo 'LogFile.label File does not exist errors' + exit 0 +fi + +awk -f - $LOGS <new( Peer => $UNIX_SOCK, ); - if (!$sock) { + if (!$sock) { print "Server maybe down, unabled to connect to $UNIX_SOCK"; exit 2; } @@ -78,7 +81,7 @@ if ($UNIX_SOCK) { PeerAddr => $SERVERADDR, PeerPort => $PORT, ); - if (!$sock) { + if (!$sock) { print "Server maybe down, unabled to connect to $SERVERADDR:$PORT"; exit 2; } @@ -86,7 +89,7 @@ if ($UNIX_SOCK) { my $client = FCGI::Client::Connection->new( sock => $sock ); -my ( $stdout, $stderr, $appstatus ) = $client->request( +my ( $stdout, $stderr, $appstatus ) = $client->request( +{ REQUEST_METHOD => 'GET', SCRIPT_FILENAME => '', @@ -112,33 +115,53 @@ while($stdout =~ /([^\n]*)\n?/g) { if ( defined $ARGV[0] and $ARGV[0] eq "config" ) { - + if($body =~ m/pool:\s+(.*?)\n/) { $pool = $1; } - print "graph_title php5-fpm status $pool\n"; - print "graph_args --base 1000 -l 0\n"; - print "graph_vlabel Processes\n"; - print "graph_scale yes\n"; - print "graph_category php-fpm\n"; - print "graph_info This graph shows the php5-fpm process manager status from pool: $pool\n"; - print "active.label Active processes\n"; - print "active.type GAUGE\n"; - print "active.draw AREA\n"; - print "active.info The number of active processes\n"; - print "idle.label Idle processes\n"; - print "idle.type GAUGE\n"; - print "idle.draw STACK\n"; - print "idle.info The number of idle processes\n"; - print "total.label Total processes\n"; - print "total.type GAUGE\n"; - print "total.draw LINE2\n"; - print "total.info The number of idle + active processes\n"; - exit 0 -} + print <<"EOF"; +multigraph ${PLUGIN_NAME}_process +graph_title php5-fpm processes for $pool +graph_args --base 1000 -l 0 +graph_vlabel Processes +graph_scale yes +graph_category php-fpm +graph_info This graph shows the php5-fpm process manager status from pool: $pool +active.label Active processes +active.type GAUGE +active.draw AREA +active.info The number of active processes +idle.label Idle processes +idle.type GAUGE +idle.draw STACK +idle.info The number of idle processes +total.label Total processes +total.type GAUGE +total.draw LINE2 +total.info The number of idle + active processes -print $body; +multigraph ${PLUGIN_NAME}_slowrequests +graph_title php5-fpm slow requests $pool +graph_args --base 1000 -l 0 +graph_vlabel Slow requests +graph_scale yes +graph_category php-fpm +graph_info This graph shows the php5-fpm slow request from pool: $pool +slow_requests.label Slow requests +slow_requests.type DERIVE +slow_requests.draw LINE2 +slow_requests.min 0 +slow_requests.info evolution of slow requests + +EOF + + exit 0 +} + +# print $body; + +print "multigraph ${PLUGIN_NAME}_process\n"; if($body =~ m/idle processes: (.*?)\n/) { $IDLE = $1; @@ -152,3 +175,10 @@ if($body =~ m/total processes: (.*?)\n/) { $TOTAL = $1; print "total.value ".$TOTAL."\n"; } + +if($body =~ m/slow requests: (.*?)\n/) { + $SLOW_REQUESTS = $1; + print "\n"; + print "multigraph ${PLUGIN_NAME}_slowrequests\n"; + print "slow_requests.value ".$SLOW_REQUESTS."\n"; +} diff --git a/plugins/postfix/postfix_mailqueue_ b/plugins/postfix/postfix_mailqueue_ index 5ee84556..8154a677 100755 --- a/plugins/postfix/postfix_mailqueue_ +++ b/plugins/postfix/postfix_mailqueue_ @@ -114,6 +114,12 @@ maildrop.label maildrop incoming.label incoming corrupt.label corrupt hold.label held +active.draw AREA +deferred.draw STACK +maildrop.draw STACK +incoming.draw STACK +corrupt.draw STACK +hold.draw STACK EOF for field in active deferred maildrop incoming corrupt hold; do print_warning $field diff --git a/plugins/postgresql/postgres_space_ b/plugins/postgresql/postgres_space_ index df1fd640..6a8931b0 100755 --- a/plugins/postgresql/postgres_space_ +++ b/plugins/postgresql/postgres_space_ @@ -67,9 +67,6 @@ if (exists $ARGV[0]) { my (undef, undef, $dbname) = split (/_/, $0, 3); die "No dbname configured (did you make the proper symlink?)" unless $dbname; -my @datasources = DBI->data_sources ('Pg') - or die ("Can't read any possible data sources: $?"); - my $dsn = "DBI:Pg:dbname=$dbname"; $dsn .= ";host=$dbhost" if $dbhost; print "#$dsn\n" if $debug; diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ index 92340439..17d3da29 100644 --- a/plugins/prosody/prosody_ +++ b/plugins/prosody/prosody_ @@ -25,7 +25,7 @@ import os import telnetlib import re -def main(): +def main(): try: mode = sys.argv[1] except IndexError: @@ -33,7 +33,7 @@ def main(): wildcard = sys.argv[0].split("prosody_")[1].split("_")[0] host = os.environ.get('host', 'localhost') port = int(os.environ.get('port', 5582)) - + if mode == "suggest": print "c2s" print "s2s" @@ -47,7 +47,7 @@ def main(): print "graph_title Prosody C2S Connections" print "graph_vlabel users" print "graph_category Prosody" - + print "all_client_connections.label client connections" print "secure_client_connections.label secure client connections" print "insecure_client_connections.label insecure client " \ @@ -64,7 +64,7 @@ def main(): secure_client_connections = int(parsed_info[0]) print "secure_client_connections.value %s" % \ (secure_client_connections) - + telnet.write("c2s:show_insecure()\n") telnet_response = telnet.read_until("insecure client connections", 5) @@ -75,19 +75,19 @@ def main(): all_client_connections = secure_client_connections + \ insecure_client_connections print "all_client_connections.value %s" % (all_client_connections) - telnet.write("quit") - + telnet.write("quit\n") + elif wildcard == "s2s": if mode == "config": print "graph_title Prosody S2S Connections" print "graph_vlabel servers" print "graph_category Prosody" - + print "outgoing_connections.label outgoing connections" print "incoming_connections.label incoming connections" sys.exit(0) - - else: + + else: server_connections_re = re.compile(r"(\d+) outgoing, (\d+)") telnet = telnetlib.Telnet(host, port) telnet.write("s2s:show()\n") @@ -95,8 +95,8 @@ def main(): parsed_info = server_connections_re.findall(telnet_response) print "outgoing_connections.value %s" % (parsed_info[0][0]) print "incoming_connections.value %s" % (parsed_info[0][1]) - telnet.write("quit") - + telnet.write("quit\n") + elif wildcard == "presence": if mode == "config": print "graph_title Prosody Client Presence" @@ -110,8 +110,8 @@ def main(): print "dnd.label Do Not Disturb Clients" sys.exit(0) - else: - client_presence_re = re.compile(r"- (.*?)\(\d+\)") + else: + client_presence_re = re.compile(r"[-\]] (.*?)\(\d+\)") telnet = telnetlib.Telnet(host, port) telnet.write("c2s:show()\n") telnet_response = telnet.read_until("clients", 5) @@ -121,7 +121,7 @@ def main(): print "away.value %s" % (parsed_info.count("away")) print "xa.value %s" % (parsed_info.count("xa")) print "dnd.value %s" % (parsed_info.count("dnd")) - telnet.write("quit") + telnet.write("quit\n") elif wildcard == "uptime": if mode == "config": @@ -147,7 +147,7 @@ def main(): uptime_value = float(parsed_info[0]) + float(parsed_info[1])/24 +\ float(parsed_info[2])/60/24 print "uptime.value %s" % (uptime_value) - telnet.write("quit") + telnet.write("quit\n") elif wildcard == "users": if mode == "config": diff --git a/plugins/redis/redis_ b/plugins/redis/redis_ index d2550b1b..2d61ca9d 100755 --- a/plugins/redis/redis_ +++ b/plugins/redis/redis_ @@ -35,9 +35,11 @@ use strict; use IO::Socket::INET; +use IO::Socket::UNIX; use Switch; my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "127.0.0.1"; +my $UNIX_SOCKET = exists $ENV{'unixsocket'} ? $ENV{'unixsocket'} : ''; # path to Redis Unix sock file my $PORT = exists $ENV{'port'} ? $ENV{'port'} : 6379; my $PASSWORD = exists $ENV{'password'} ? $ENV{'password'} : undef; my $TITLE_PREFIX = exists $ENV{'title_prefix'} ? $ENV{'title_prefix'} . ": " : ""; @@ -208,12 +210,26 @@ switch ($0) { close ($sock); sub get_conn { - my $sock = IO::Socket::INET->new( - PeerAddr => $HOST, - PeerPort => $PORT, - Timeout => 10, - Proto => 'tcp' - ); + + my $sock; + + if( $UNIX_SOCKET && -S $UNIX_SOCKET ){ + + $sock = IO::Socket::UNIX->new( + Type => SOCK_STREAM(), + Peer => $UNIX_SOCKET, + ); + + }else{ + + $sock = IO::Socket::INET->new( + PeerAddr => $HOST, + PeerPort => $PORT, + Timeout => 10, + Proto => 'tcp' + ); + } + if ( defined( $PASSWORD ) ) { print $sock "AUTH ", $PASSWORD, "\r\n"; my $result = <$sock> || die "can't read socket: $!"; diff --git a/plugins/sensors/battery_ b/plugins/sensors/battery_ new file mode 100755 index 00000000..01f6f9d0 --- /dev/null +++ b/plugins/sensors/battery_ @@ -0,0 +1,68 @@ +#! /bin/sh +# Plugin to monitor the battery status via the uevent API +# +# (c) 2015 - GPLv2 - steve.schnepp@pwkf.org +# +# It is a wildcard plugin, symlink it with the battery directory +# default is to display charge as mAh, but you can also use percentage if you +# prefer, by setting the env var "percent" to "yes". +# +# [battery_*] +# env.percent no +# + +battery_name=${0##*_} +percent=${percent:-"no"} + +if [ "$1" = "config" ] +then + echo "graph_title Battery $battery_name" + if [ "$percent" = "yes" ] + then + echo "graph_vlabel %" + else + echo "graph_vlabel mAh" + fi + + echo "charge_design.label Design charge" + echo "charge_design.draw AREA" + [ "$percent" = "yes" ] && echo "charge_design.cdef charge_design,charge_design,/,100,*" + + echo "charge_full.label Full charge" + echo "charge_full.draw AREA" + [ "$percent" = "yes" ] && echo "charge_full.cdef charge_full,charge_design,/,100,*" + echo "charge_now.label Current charge" + echo "charge_now.draw AREA" + [ "$percent" = "yes" ] && echo "charge_now.cdef charge_now,charge_design,/,100,*" + + exit 0 +fi + +# Crudely read all the vars into the current namespace +. /sys/class/power_supply/$battery_name/uevent + +echo "charge_design.value $(( $POWER_SUPPLY_CHARGE_FULL_DESIGN / 1000 )) " +echo "charge_full.value $(( $POWER_SUPPLY_CHARGE_FULL / 1000 ))" +echo "charge_now.value $(( $POWER_SUPPLY_CHARGE_NOW / 1000 ))" + +exit 0 + + +:<< DATA +cat /sys/class/power_supply/$1/uevent +POWER_SUPPLY_NAME=CMB1 +POWER_SUPPLY_STATUS=Charging +POWER_SUPPLY_PRESENT=1 +POWER_SUPPLY_TECHNOLOGY=Li-ion +POWER_SUPPLY_CYCLE_COUNT=0 +POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000 +POWER_SUPPLY_VOLTAGE_NOW=11418000 +POWER_SUPPLY_CURRENT_NOW=2668000 +POWER_SUPPLY_CHARGE_FULL_DESIGN=5200000 +POWER_SUPPLY_CHARGE_FULL=5000000 +POWER_SUPPLY_CHARGE_NOW=100000 +POWER_SUPPLY_CAPACITY=2 +POWER_SUPPLY_CAPACITY_LEVEL=Normal +POWER_SUPPLY_MODEL_NAME=CP293570 +POWER_SUPPLY_MANUFACTURER=Fujitsu +DATA diff --git a/plugins/snmp/snmp__cisco_sbs_cpu b/plugins/snmp/snmp__cisco_sbs_cpu new file mode 100644 index 00000000..9dd58a3c --- /dev/null +++ b/plugins/snmp/snmp__cisco_sbs_cpu @@ -0,0 +1,90 @@ +#!/usr/bin/perl -w +# -*- perl -*- +# vim: ft=perl + +=head1 NAME + +snmp__cisco_sbs_cpu - Munin plugin to monitor CPU Usage on Cisco Small Business Switches. + +=head1 APPLICABLE SYSTEMS + +Cisco Small Business Switches (SBXXXX devices) + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + + [snmp_*] + env.version 2 + env.community public + +In general SNMP is not very secure at all unless you use SNMP version +3 which supports authentication and privacy (encryption). But in any +case the community string for your devices should not be "public". + +Please see 'perldoc Munin::Plugin::SNMP' for further configuration +information. + +=head1 INTERPRETATION + +CPU Percentage gives an idea of utilization of the device. High CPU +can indicate a configuration problem or overutilization of the device. + +=head1 MAGIC MARKERS + + #%# family=snmpauto + #%# capabilities=snmpconf + +=head1 VERSION + + $Id$ + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2015 James DeVincentis + +=head1 LICENSE + +GPLv3. + +=cut + +use strict; +use Munin::Plugin::SNMP; + +if (defined $ARGV[0] and $ARGV[0] eq 'snmpconf') { + print "require 1.3.6.1.4.1.9.6.1.101.1.7.0 [0-9]\n"; + exit 0; +} + +if (defined $ARGV[0] and $ARGV[0] eq "config") { + my ($host) = Munin::Plugin::SNMP->config_session(); + + print "host_name $host\n" unless $host eq 'localhost'; + print <<"EOF"; +graph_title CPU Utilization +graph_args --base 1000 -l 0 -u 100 +graph_vlabel CPU % +graph_category system +graph_info This graph shows the percentage of CPU used at different intervals. High CPU Utilization can indicate a configuration problem or overutilization of the device. +load5sec.label 5s +load5sec.info 5 Second CPU Utilization Average +load5sec.draw LINE1 +load1min.label 1m +load1min.info 1 Minute CPU Utilization Average +load1min.draw LINE1 +load5min.label 5m +load5min.info 5 Minute CPU Utilization Average +EOF + exit 0; +} + +my $session = Munin::Plugin::SNMP->session(); +print "load5sec.value ", $session->get_single('1.3.6.1.4.1.9.6.1.101.1.7.0'), "\n"; +print "load1min.value ", $session->get_single('1.3.6.1.4.1.9.6.1.101.1.8.0'), "\n"; +print "load5min.value ", $session->get_single('1.3.6.1.4.1.9.6.1.101.1.9.0'), "\n"; diff --git a/plugins/snmp/snmp__synology_hddtemp b/plugins/snmp/snmp__synology_hddtemp new file mode 100644 index 00000000..57931c3d --- /dev/null +++ b/plugins/snmp/snmp__synology_hddtemp @@ -0,0 +1,109 @@ +#!/usr/bin/perl -w +# -*- perl -*- +# vim: ft=perl + +=head1 NAME + +snmp__syno_hddtemp - Munin plugin to monitor the temperature of +harddisks in an Synology NAS. + +=head1 APPLICABLE SYSTEMS + +Any Synology NAS device which provides the synoDisk MIB. + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + + [snmp_*] + env.version 2 + env.community public + +In general SNMP is not very secure at all unless you use SNMP version +3 which supports authentication and privacy (encryption). But in any +case the community string for your devices should not be "public". + +Please see 'perldoc Munin::Plugin::SNMP' for further configuration +information. + +=head1 INTERPRETATION + +The temperature of each disk installed in °C. + +=head1 MIB INFORMATION + +This plugin requires support for the synoDisk. It reports +the temperature of the installed disks. + +=head1 MAGIC MARKERS + + #%# family=snmpauto + #%# capabilities=snmpconf + +=head1 VERSION + + $Id$ + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2015 Thomas Arthofer + +This plugin was derived from snmp__netstat by Lars Strand with updates +by Matthew Boyle. + +=head1 LICENSE + +GPLv2. + +=cut + +use strict; +use Munin::Plugin::SNMP; + +my $oid_drives = '1.3.6.1.4.1.6574.2.1.1'; + +if (defined $ARGV[0] and $ARGV[0] eq 'snmpconf') { + print "require ${oid_drives}. [0-9]\n"; + exit 0; +} + +my ($session, $error) = Munin::Plugin::SNMP->session(); + +my $table = $session->get_hash( + -baseoid => $oid_drives, # IF-MIB + -cols => { + 2 => 'name', + 3 => 'type', + 6 => 'temp', + } + ); + +if (defined $ARGV[0] and $ARGV[0] eq 'config') { + my ($host) = Munin::Plugin::SNMP->config_session(); + + print "host_name $host\n" unless $host eq 'localhost'; + print "graph_title HDD temperature\n"; + print "graph_category sensors\n"; + print "graph_vlabel Degrees Celsius\n"; + print "graph_info This graph shows the temperature of all HDDs in the Diskstation.\n"; + + foreach my $key ( sort keys %$table ) + { + print "temp$key.label $table->{$key}->{'name'}\n"; + print "temp$key.info Temperature of $table->{$key}->{'name'} ($table->{$key}->{'type'})\n"; + } + exit 0; +} + + +my $names = $session->get_entries(-columns => [ $oid_drives ]); + +foreach my $key ( sort keys %$table ) +{ + print "temp$key.value $table->{$key}->{'temp'}\n"; +} diff --git a/plugins/snmp/snmp__synology_temperature b/plugins/snmp/snmp__synology_temperature new file mode 100644 index 00000000..522dfa75 --- /dev/null +++ b/plugins/snmp/snmp__synology_temperature @@ -0,0 +1,91 @@ +#!/usr/bin/perl -w +# -*- cperl -*- +# vim: ft=perl + +=head1 NAME + +snmp__syno_temperature - Munin plugin to retrieve current temperature from a +Synology NAS. + +=head1 APPLICABLE SYSTEMS + +Any Synology NAS device which provides the synoSystem MIB. + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + + [snmp_*] + env.version 2 + env.community public + +In general SNMP is not very secure at all unless you use SNMP version +3 which supports authentication and privacy (encryption). But in any +case the community string for your devices should not be "public". + +Please see 'perldoc Munin::Plugin::SNMP' for further configuration +information. + +=head1 INTERPRETATION + +This plugin queries the current temperature of the NAS. + +=head1 MIB INFORMATION + +This plugin requires support for the synoSystem MIB by Synology. +It reports the contents of the temperature OID. + +=head1 MAGIC MARKERS + + #%# family=snmpauto + #%# capabilities=snmpconf + +=head1 VERSION + + $Id$ + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2015 Thomas Arthofer + +=head1 LICENSE + +GPLv2 or (at your option) any later version. + +=cut + +use strict; +use Munin::Plugin::SNMP; + +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { + print "require 1.3.6.1.4.1.6574.1.2.0 [0-9]\n"; # Number + exit 0; +} + +if (defined $ARGV[0] and $ARGV[0] eq "config") { + my ($host) = Munin::Plugin::SNMP->config_session(); + print "host_name $host\n" unless $host eq 'localhost'; + print "graph_title Temperatures +graph_args --base 1000 -l 0 +graph_vlabel Degrees Celsius +graph_category sensors +graph_info This graph shows the temperature of the diskstation. +temp.label CPU +temp.info The temperature of the onboard CPU. +"; + exit 0; +} + +my $session = Munin::Plugin::SNMP->session(-translate => + [ -timeticks => 0x0 ]); + +my $temp = $session->get_single (".1.3.6.1.4.1.6574.1.2.0") || 'ERROR'; + +print "Retrived uptime is '$temp'\n" if $Munin::Plugin::SNMP::DEBUG; + +print "temp.value ", $temp, "\n"; diff --git a/plugins/snmp/snmp__synology_ups b/plugins/snmp/snmp__synology_ups new file mode 100644 index 00000000..2ceea9e5 --- /dev/null +++ b/plugins/snmp/snmp__synology_ups @@ -0,0 +1,102 @@ +#!/usr/bin/perl -w +# -*- cperl -*- +# vim: ft=perl + +=head1 NAME + +snmp__syno_ups - Munin plugin to retrieve various information of the +UPS attached to a Synology NAS. + +=head1 APPLICABLE SYSTEMS + +Any Synology NAS device which provides the synoUPS MIB. + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + + [snmp_*] + env.version 2 + env.community public + +In general SNMP is not very secure at all unless you use SNMP version +3 which supports authentication and privacy (encryption). But in any +case the community string for your devices should not be "public". + +Please see 'perldoc Munin::Plugin::SNMP' for further configuration +information. + +=head1 INTERPRETATION + +The plugin reports the following stats about the UPS attached: + - Load in % + - Charge in % + +=head1 MIB INFORMATION + +This plugin requires support for the synoUPS MIB by Synology. + +=head1 MAGIC MARKERS + + #%# family=snmpauto + #%# capabilities=snmpconf + +=head1 VERSION + + $Id$ + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2015 Thomas Arthofer + +=head1 LICENSE + +GPLv2 or (at your option) any later version. + +=cut + +use strict; +use Munin::Plugin::SNMP; + +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { + print "require 1.3.6.1.4.1.6574.4.3.1.1.0 [0-9]\n"; # Charge + print "require 1.3.6.1.4.1.6574.4.2.12.1.0 [0-9]\n"; # Load + exit 0; +} + +if (defined $ARGV[0] and $ARGV[0] eq "config") { + my ($host) = Munin::Plugin::SNMP->config_session(); + print "host_name $host\n" unless $host eq 'localhost'; + print "graph_title UPS +graph_args --base 1000 -l 0 +graph_vlabel Status of UPS +graph_category system +graph_info This graph shows the status of the attached UPS. +charge.label Charge +charge.info Charge status of battery. +charge.draw LINE2 +load.label Load +load.info Load on the UPS +"; + exit 0; +} + +my $session = Munin::Plugin::SNMP->session(-translate => + [ -timeticks => 0x0 ]); + +my $charge = $session->get_single (".1.3.6.1.4.1.6574.4.3.1.1.0") || 'ERROR'; +$charge = unpack "f", reverse pack "H*", $charge; + +my $load = $session->get_single (".1.3.6.1.4.1.6574.4.2.12.1.0") || 'ERROR'; +$load = unpack "f", reverse pack "H*", $load; + +print "Retrived charge '$charge'\n" if $Munin::Plugin::SNMP::DEBUG; +print "Retrived load '$load'\n" if $Munin::Plugin::SNMP::DEBUG; + +print "charge.value ", $charge, "\n"; +print "load.value ", $load, "\n"; diff --git a/plugins/streaming/icecast2_stats_ b/plugins/streaming/icecast2_stats_ new file mode 100755 index 00000000..ea6a159c --- /dev/null +++ b/plugins/streaming/icecast2_stats_ @@ -0,0 +1,182 @@ +#!/usr/bin/python3 +# +# This plugin shows the statistics of every source currently connected to the Icecast2 server. +# See the Icecast2_ plugin for collecting data of specific mountpoints. +# +# An icecast server v2.4 or later is required for this module since it uses the status-json.xsl +# output (see http://www.icecast.org/docs/icecast-2.4.1/server-stats.html). +# +# The following data for each source is available: +# * listeners: current count of listeners +# * duration: the age of the stream/source +# +# Additionally the Icecast service uptime is available. +# +# This plugin requires Python 3 (e.g. for urllib instead of urllib2). +# +# +# Environment variables: +# * status_url: defaults to "http://localhost:8000/status-json.xsl" +# +# +# Copyright (C) 2015 Lars Kruse +# +# 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 . +# +# +# Magic markers +#%# capabilities=autoconf suggest +#%# family=auto + + +import datetime +import json +import os +import urllib.request +import sys + + +status_url = os.getenv("status_url", "http://localhost:8000/status-json.xsl") +PLUGIN_SCOPES = ("sources_listeners", "sources_duration", "service_uptime") +PLUGIN_NAME_PREFIX = "icecast2_stats_" + + +def clean_fieldname(name): + """ see http://munin-monitoring.org/wiki/notes_on_datasource_names + + This function is a bit clumsy as it tries to avoid using a regular + expression for the sake of micropython compatibility. + """ + def get_valid(char, position): + if char == '_': + return '_' + elif 'a' <= char.lower() <= 'z': + return char + elif (position > 0) and ('0' <= char <= '9'): + return char + else: + return '_' + return "".join([get_valid(char, position) for position, char in enumerate(name)]) + + +def parse_iso8601(datestring): + """ try to avoid using an external library for parsing an ISO8601 date string """ + if datestring.endswith("Z"): + timestamp_string = datestring[:-1] + time_delta = datetime.timedelta(minutes=0) + else: + # the "offset_text" is something like "+0500" or "-0130" + timestamp_string, offset_text = datestring[:-5], datestring[-5:] + offset_minutes = int(offset_text[1:3]) * 60 + int(offset_text[3:]) + if offset_text.startswith("+"): + pass + elif offset_text.startswith("-"): + offset_minutes *= -1 + else: + # invalid format + return None + time_delta = datetime.timedelta(minutes=offset_minutes) + local_time = datetime.datetime.strptime(timestamp_string, "%Y-%m-%dT%H:%M:%S") + return local_time + time_delta + + +def get_iso8601_age_days(datestring): + now = datetime.datetime.now() + timestamp = parse_iso8601(datestring) + if timestamp: + return (now - timestamp).total_seconds() / (24 * 60 * 60) + else: + return None + + +def _get_json_statistics(): + with urllib.request.urlopen(status_url) as conn: + json_body = conn.read() + return json.loads(json_body.decode("utf-8")) + + +def get_sources(): + sources = [] + for source in _get_json_statistics()["icestats"]["source"]: + path_name = source["listenurl"].split("/")[-1] + sources.append({"name": path_name, + "fieldname": clean_fieldname(path_name), + "listeners": source["listeners"], + "duration_days": get_iso8601_age_days(source["stream_start_iso8601"])}) + sources.sort(key=lambda item: item["name"]) + return sources + + +def get_server_uptime_days(): + return get_iso8601_age_days(_get_json_statistics()["icestats"]["server_start_iso8601"]) + + +def get_scope(): + called_name = os.path.basename(sys.argv[0]) + if called_name.startswith(PLUGIN_NAME_PREFIX): + scope = called_name[len(PLUGIN_NAME_PREFIX):] + if not scope in PLUGIN_SCOPES: + print("Invalid scope requested: {0} (expected: {1})".format(scope, "/".join(PLUGIN_SCOPES)), file=sys.stderr) + sys.exit(2) + else: + print("Invalid filename - failed to discover plugin scope", file=sys.stderr) + sys.exit(2) + return scope + + +if __name__ == "__main__": + action = sys.argv[1] if (len(sys.argv) > 1) else None + if action == "autoconf": + try: + get_sources() + print("yes") + except OSError: + print("no") + elif action == "suggest": + for scope in PLUGIN_SCOPES: + print(scope) + elif action == "config": + scope = get_scope() + if scope == "sources_listeners": + print("graph_title Total number of listeners") + print("graph_vlabel listeners") + print("graph_category Icecast") + for index, source in enumerate(get_sources()): + print("{0}.label {1}".format(source["fieldname"], source["name"])) + print("{0}.draw {1}".format(source["fieldname"], ("AREA" if (index == 0) else "STACK"))) + elif scope == "sources_duration": + print("graph_title Duration of sources") + print("graph_vlabel duration in days") + print("graph_category Icecast") + for source in get_sources(): + print("{0}.label {1}".format(source["fieldname"], source["name"])) + elif scope == "service_uptime": + print("graph_title Icecast service uptime") + print("graph_vlabel uptime in days") + print("graph_category Icecast") + print("uptime.label service uptime") + elif action is None: + scope = get_scope() + if scope == "sources_listeners": + for source in get_sources(): + print("{0}.value {1}".format(source["fieldname"], source["listeners"])) + elif scope == "sources_duration": + for source in get_sources(): + print("{0}.value {1}".format(source["fieldname"], source["duration_days"] or 0)) + elif scope == "service_uptime": + print("uptime.value {0}".format(get_server_uptime_days())) + else: + print("Invalid argument given: {0}".format(action), file=sys.stderr) + sys.exit(1) + sys.exit(0) diff --git a/plugins/system/1sec/if1sec-c.c b/plugins/system/1sec/if1sec-c.c new file mode 100644 index 00000000..6793be4c --- /dev/null +++ b/plugins/system/1sec/if1sec-c.c @@ -0,0 +1,273 @@ +/* + * if1sec C plugin + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define PROC_STAT "/proc/net/dev" +#define PLUGIN_NAME "if1sec-c" + +int fail(char* msg) { + perror(msg); + + return 1; +} + +/* Returns the ifname from a /proc/net/dev line + * It will return an inside pointer to line, and modifiy the end with a \0 + */ +char* get_ifname_from_procstatline(char* line) { + char *ifname; + for (ifname = line; (*ifname) == ' '; ifname ++); + + char *ifname_end; + for (ifname_end = ifname; (*ifname_end) != ':'; ifname_end ++); + (*ifname_end) = '\0'; + + return ifname; +} + +int config() { + /* Get the number of if */ + int f; + if ( !(f=open(PROC_STAT, O_RDONLY)) ) { + return fail("cannot open " PROC_STAT); + } + + // Starting with -2, since the 2 lines on top are header lines + int nif = -2; + + const int buffer_size = 64 * 1024; + char buffer[buffer_size]; + + // whole /proc/stat can be read in 1 syscall + if (read(f, buffer, buffer_size) <= 0) { + return fail("cannot read " PROC_STAT); + } + + // tokenization per-line + char* line; char *saveptr; + char* newl = "\n"; + for (line = strtok_r(buffer, newl, &saveptr); line; line = strtok_r(NULL, newl, &saveptr)) { + // Skip the header lines + if (nif ++ < 0) { continue; } + + char* if_name = get_ifname_from_procstatline(line); + printf( + "multigraph if_%s_1sec" "\n" + "graph_order down up" "\n" + "graph_title %s traffic" "\n" + "graph_category system::1sec" "\n" + "graph_vlabel bits in (-) / out (+) per ${graph_period}" "\n" + "graph_data_size custom 1d, 10s for 1w, 1m for 1t, 5m for 1y" "\n" + , if_name, if_name + ); + + printf( + "down.label -" "\n" + "down.type DERIVE" "\n" + "down.graph no" "\n" + "down.cdef down,8,*" "\n" + "down.min 0" "\n" + + "up.label bps" "\n" + "up.type DERIVE" "\n" + "up.negative down" "\n" + "up.cdef down,8,*" "\n" + "up.min 0" "\n" + ); + } + + close(f); + + + return 0; +} + +char* pid_filename; +char* cache_filename; + +/* Wait until the next second, and return the EPOCH */ +time_t wait_until_next_second() { + struct timespec tp; + clock_gettime(CLOCK_REALTIME, &tp); + + time_t current_epoch = tp.tv_sec; + long nsec_to_sleep = 1000*1000*1000 - tp.tv_nsec; + + + /* Only sleep if needed */ + if (nsec_to_sleep > 0) { + tp.tv_sec = 0; + tp.tv_nsec = nsec_to_sleep; + nanosleep(&tp, NULL); + } + + return current_epoch + 1; +} + +int acquire() { + + /* fork ourselves if not asked otherwise */ + char* no_fork = getenv("no_fork"); + if (! no_fork || strcmp("1", no_fork)) { + if (fork()) return; + // we are the child, complete the daemonization + + /* Close standard IO */ + fclose(stdin); + fclose(stdout); + fclose(stderr); + + /* create new session and process group */ + setsid(); + } + + /* write the pid */ + FILE* pid_file = fopen(pid_filename, "w"); + fprintf(pid_file, "%d\n", getpid()); + fclose(pid_file); + + /* Reading /proc/stat */ + int f = open(PROC_STAT, O_RDONLY); + + /* open the spoolfile */ + int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR); + + /* loop each second */ + while (1) { + /* wait until next second */ + time_t epoch = wait_until_next_second(); + + const int buffer_size = 64 * 1024; + char buffer[buffer_size]; + + if (lseek(f, 0, SEEK_SET) < 0) { + return fail("cannot seek " PROC_STAT); + } + + // whole PROC file can be read in 1 syscall + if (read(f, buffer, buffer_size) <= 0) { + return fail("cannot read " PROC_STAT); + } + + // ignore the 1rst line + char* line; char *saveptr; + const char* newl = "\n"; + + /* lock */ + flock(cache_file, LOCK_EX); + + int nif = -2; + for (line = strtok_r(buffer, newl, &saveptr); line; line = strtok_r(NULL, newl, &saveptr)) { + // Skip the header lines + if (nif ++ < 0) { continue; } + + char if_id[64]; + uint_fast64_t r_bytes, r_packets, r_errs, r_drop, r_fifo, r_frame, r_compressed, r_multicast; + uint_fast64_t t_bytes, t_packets, t_errs, t_drop, t_fifo, t_frame, t_compressed, t_multicast; + sscanf(line, "%s" + " " + "%llu %llu %llu %llu %llu %llu %llu %llu" + " " + "%llu %llu %llu %llu %llu %llu %llu %llu" + , if_id + , &r_bytes, &r_packets, &r_errs, &r_drop, &r_fifo, &r_frame, &r_compressed, &r_multicast + , &t_bytes, &t_packets, &t_errs, &t_drop, &t_fifo, &t_frame, &t_compressed, &t_multicast + ); + + // Remove trailing ':' of if_id + if_id[strlen(if_id) - 1] = '\0'; + + char out_buffer[1024]; + sprintf(out_buffer, + "multigraph if_%s_1sec" "\n" + "up.value %ld:%llu" "\n" + "down.value %ld:%llu" "\n" + , if_id + , epoch, r_bytes + , epoch, t_bytes + ); + + write(cache_file, out_buffer, strlen(out_buffer)); + } + + /* unlock */ + flock(cache_file, LOCK_UN); + } + + close(cache_file); + close(f); + + return 0; +} + +int fetch() { + FILE* cache_file = fopen(cache_filename, "r+"); + + /* lock */ + flock(fileno(cache_file), LOCK_EX); + + /* cat the cache_file to stdout */ + char buffer[1024]; + while (fgets(buffer, 1024, cache_file)) { + printf("%s", buffer); + } + + ftruncate(fileno(cache_file), 0); + fclose(cache_file); + + return 0; +} + +int main(int argc, char **argv) { + /* resolve paths */ + char *MUNIN_PLUGSTATE = getenv("MUNIN_PLUGSTATE"); + + /* Default is current directory */ + if (! MUNIN_PLUGSTATE) MUNIN_PLUGSTATE = "."; + + size_t MUNIN_PLUGSTATE_length = strlen(MUNIN_PLUGSTATE); + + pid_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/" PLUGIN_NAME ".") + strlen("pid") + 1); pid_filename[0] = '\0'; + cache_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/" PLUGIN_NAME ".") + strlen("value") + 1); cache_filename[0] = '\0'; + + strcat(pid_filename, MUNIN_PLUGSTATE); + strcat(pid_filename, "/" PLUGIN_NAME "." "pid"); + + strcat(cache_filename, MUNIN_PLUGSTATE); + strcat(cache_filename, "/" PLUGIN_NAME "." "value"); + + if (argc > 1) { + char* first_arg = argv[1]; + if (! strcmp(first_arg, "config")) { + return config(); + } + + if (! strcmp(first_arg, "acquire")) { + return acquire(); + } + } + + return fetch(); +} + +/***** DEMO + +/proc/net/dev sample + +Inter-| Receive | Transmit + face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed + lo: 4364 54 0 0 0 0 0 0 4364 54 0 0 0 0 0 0 + eth0: 3459461624 22016512 0 70 0 0 0 0 3670486138 18117144 0 0 0 0 0 0 + +*****/ diff --git a/plugins/system/multicpu1sec/.gitignore b/plugins/system/multicpu1sec/.gitignore new file mode 100644 index 00000000..4ff4b96f --- /dev/null +++ b/plugins/system/multicpu1sec/.gitignore @@ -0,0 +1,4 @@ +/multicpu1sec-c +/multicpu1sec.o +/multicpu1sec.pid +/multicpu1sec.value diff --git a/plugins/system/multicpu1sec/multicpu1sec-c.c b/plugins/system/multicpu1sec/multicpu1sec-c.c index b66eab79..3e018ad8 100644 --- a/plugins/system/multicpu1sec/multicpu1sec-c.c +++ b/plugins/system/multicpu1sec/multicpu1sec-c.c @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -20,23 +21,30 @@ int fail(char* msg) { int config() { /* Get the number of CPU */ - FILE* f; - if ( !(f=fopen(PROC_STAT, "r")) ) { + int f; + if ( !(f=open(PROC_STAT, O_RDONLY)) ) { return fail("cannot open " PROC_STAT); } // Starting with -1, since the first line is the "global cpu line" int ncpu = -1; - while (! feof(f)) { - char buffer[1024]; - if (fgets(buffer, 1024, f) == 0) { - break; - } - if (! strncmp(buffer, "cpu", 3)) ncpu ++; + const int buffer_size = 64 * 1024; + char buffer[buffer_size]; + + // whole /proc/stat can be read in 1 syscall + if (read(f, buffer, buffer_size) <= 0) { + return fail("cannot read " PROC_STAT); } - fclose(f); + // tokenization per-line + char* line; + char* newl = "\n"; + for (line = strtok(buffer, newl); line; line = strtok(NULL, newl)) { + if (! strncmp(line, "cpu", 3)) ncpu ++; + } + + close(f); printf( "graph_title multicpu1sec\n" @@ -104,43 +112,62 @@ int acquire() { fprintf(pid_file, "%d\n", getpid()); fclose(pid_file); + /* Reading /proc/stat */ + int f = open(PROC_STAT, O_RDONLY); + + /* open the spoolfile */ + int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY); + /* loop each second */ while (1) { /* wait until next second */ time_t epoch = wait_until_next_second(); - /* Reading /proc/stat */ - FILE* f = fopen(PROC_STAT, "r"); - // Read and ignore the 1rst line - char buffer[1024]; - fgets(buffer, 1024, f); - /* open the spoolfile */ - FILE* cache_file = fopen(cache_filename, "a"); + const int buffer_size = 64 * 1024; + char buffer[buffer_size]; + + if (lseek(f, 0, SEEK_SET) < 0) { + return fail("cannot seek " PROC_STAT); + } + + // whole /proc/stat can be read in 1 syscall + if (read(f, buffer, buffer_size) <= 0) { + return fail("cannot read " PROC_STAT); + } + + // ignore the 1rst line + char* line; + const char* newl = "\n"; + line = strtok(buffer, newl); + /* lock */ - flock(fileno(cache_file), LOCK_EX); - - while (! feof(f)) { - if (fgets(buffer, 1024, f) == 0) { - // EOF - break; - } + flock(cache_file, LOCK_EX); + for (line = strtok(NULL, newl); line; line = strtok(NULL, newl)) { // Not on CPU lines anymore - if (strncmp(buffer, "cpu", 3)) break; + if (strncmp(line, "cpu", 3)) break; char cpu_id[64]; long usr, nice, sys, idle, iowait, irq, softirq; - sscanf(buffer, "%s %ld %ld %ld %ld %ld %ld %ld", cpu_id, &usr, &nice, &sys, &idle, &iowait, &irq, &softirq); + sscanf(line, "%s %ld %ld %ld %ld %ld %ld %ld", cpu_id, &usr, &nice, &sys, &idle, &iowait, &irq, &softirq); long used = usr + nice + sys + iowait + irq + softirq; - fprintf(cache_file, "%s.value %ld:%ld\n", cpu_id, epoch, used); + char out_buffer[1024]; + sprintf(out_buffer, "%s.value %ld:%ld\n", cpu_id, epoch, used); + + write(cache_file, out_buffer, strlen(out_buffer)); } - fclose(cache_file); - fclose(f); + /* unlock */ + flock(cache_file, LOCK_UN); } + + close(cache_file); + close(f); + + return 0; } int fetch() { @@ -157,6 +184,8 @@ int fetch() { ftruncate(fileno(cache_file), 0); fclose(cache_file); + + return 0; } int main(int argc, char **argv) { diff --git a/plugins/time/ntpdate_ b/plugins/time/ntpdate_ index 34778c80..bb62d5bd 100755 --- a/plugins/time/ntpdate_ +++ b/plugins/time/ntpdate_ @@ -30,6 +30,7 @@ fi if [ "$1" = "config" ]; then echo "graph_title NTP offset and delay to peer $PEER" + echo "graph_category time" echo "graph_args --base 1000 --vertical-label msec" echo "offset.label Offset" echo "offset.draw LINE2" diff --git a/plugins/ups/apc_status b/plugins/ups/apc_status index fee73ff5..9860dd24 100755 --- a/plugins/ups/apc_status +++ b/plugins/ups/apc_status @@ -1,33 +1,55 @@ #!/bin/sh # -# (c) Andreas Kreisl +# (c) Andreas Kreisl extended by Tobias Schramm # # Link name will be used as title: apc_{$title} # # env.keys LOADPCT BCHARGE LINEV BATTV TIMELEFT -# env.unit % or Volt or Minutes # - if [ -z "$keys" ]; then - keys="TIMELEFT" + keys="LINEV LOADPCT BCHARGE NUMXFERS TIMELEFT" fi +apcinfo=`/sbin/apcaccess` + if [ "$1" = "config" ]; then title=`basename $0 | sed 's/^apc_//g' | awk '{ sub(/^./,toupper(substr($0,1,1))); print; }'` - echo "graph_title APC Status - $title" - echo 'graph_args --base 1000 -l 0 ' - echo "graph_vlabel $unit" - echo 'graph_category sensors' + echo 'multigraph apc_status' + echo "graph_title UPS Status - $title" + echo 'graph_args --base 1000' + echo 'graph_category hardware' title=`/sbin/apcaccess | egrep "^MODEL" | awk '{print $3" "$4" "$5" "$6" "$7" "$8" "$9;}'` echo "graph_info $title" for key in $keys; do echo "$key.label $key" - echo "$key.info Value of $key." - echo "$key.draw LINE2" + echo "$key.info Value of $key" + echo "$key.draw LINE1" + done + for key in $keys; do + key_lower=`echo "$key" | awk '{print tolower($0);}'` + unit=`echo "$apcinfo" | egrep "^$key" | awk '{print $4;}'` + echo "multigraph apc_status.$key_lower" + echo "graph_title $key" + echo 'graph_args --base 1000' + if [ -n "$unit" ]; then + echo "graph_vlabel $unit" + fi + echo 'graph_category hardware' + echo "$key.label $key" + echo "$key.info $key." + echo "$key.draw LINE1" done exit 0 fi -searchkey=`echo "$keys" | tr " " "\|"` -/sbin/apcaccess | egrep "$searchkey" | awk '{print $1".value "$3;}' +echo 'multigraph apc_status' +for key in $keys; do + echo "$apcinfo" | egrep "^$key" | awk '{print $1".value "$3;}' +done +for key in $keys; do + key_lower=`echo "$key" | awk '{print tolower($0)}'` + echo "multigraph apc_status.$key_lower" + echo "$apcinfo" | egrep "^$key" | awk '{print $1".value "$3;}' +done + diff --git a/plugins/varnish4/README.md b/plugins/varnish4/README.md index c861ce3b..f8700e85 100644 --- a/plugins/varnish4/README.md +++ b/plugins/varnish4/README.md @@ -19,9 +19,12 @@ your actual plugins directory. In your plugins.conf add ``` [varnish4_*] + group varnish env.varnishstat varnishstat env.name ``` +`group varnish` Since Varnish version 4.1, Varnish shared log utilities must be run in a context with *varnish* group membership. + `env.varnishstat` can be a full path to varnishstat if it's not in the path already. diff --git a/plugins/varnish4/varnish4_ b/plugins/varnish4/varnish4_ index ed0833e1..fa400c1d 100644 --- a/plugins/varnish4/varnish4_ +++ b/plugins/varnish4/varnish4_ @@ -34,6 +34,7 @@ The plugin needs to be able to execute varnishstat. The configuration section shows the defaults [varnish4_*] + group varnish env.varnishstat varnishstat env.name @@ -232,7 +233,7 @@ my %ASPECTS = ( 'order' => 'client_req cache_hit cache_miss ' . 'cache_hitpass' , 'vlabel' => '%', - 'args' => '-u 100 --rigid', + 'args' => '-l 0 -u 100 --rigid', 'scale' => 'no', 'values' => { 'client_req' => { @@ -630,15 +631,15 @@ my %ASPECTS = ( }, 'bans_tested' => { 'type' => 'DERIVE', - 'min' => '0' + 'min' => '0' }, 'bans_obj_killed' => { 'type' => 'DERIVE', - 'min' => '0' + 'min' => '0' }, 'bans_tests_tested' => { 'type' => 'DERIVE', - 'min' => '0' + 'min' => '0' }, 'bans_dups' => { 'type' => 'GAUGE' @@ -750,6 +751,7 @@ my %ASPECTS = ( }, 'sess_pipe_overflow' => { 'type' => 'DERIVE' + } } }, @@ -778,9 +780,9 @@ my %ASPECTS = ( sub translate_type { my $d = $_[0]; - if ($d eq "i") { + if ($d eq "i" or $d eq "g") { $d = "GAUGE"; - } elsif ($d eq "a") { + } elsif ($d eq "a" or $d eq "c") { $d = "DERIVE"; } return $d; diff --git a/plugins/virtualization/kvm_cpu b/plugins/virtualization/kvm_cpu index eb47ee96..d8ec7934 100755 --- a/plugins/virtualization/kvm_cpu +++ b/plugins/virtualization/kvm_cpu @@ -68,14 +68,14 @@ def find_vm_names(pids): result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) return result def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' - pid = Popen("pidof kvm", shell=True, stdout=PIPE) + pid = Popen("pidof qemu-system-x86_64", shell=True, stdout=PIPE) return pid.communicate()[0].split() def fetch(vms): diff --git a/plugins/virtualization/kvm_io b/plugins/virtualization/kvm_io index 5019fbdc..065f1a19 100755 --- a/plugins/virtualization/kvm_io +++ b/plugins/virtualization/kvm_io @@ -85,14 +85,14 @@ def find_vm_names(pids): result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) return result def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' - pid = Popen("pidof kvm", shell=True, stdout=PIPE) + pid = Popen("pidof qemu-system-x86_64", shell=True, stdout=PIPE) return pid.communicate()[0].split() if __name__ == "__main__": diff --git a/plugins/virtualization/kvm_mem b/plugins/virtualization/kvm_mem index 5038f9b1..66e7f399 100755 --- a/plugins/virtualization/kvm_mem +++ b/plugins/virtualization/kvm_mem @@ -82,14 +82,14 @@ def find_vm_names(pids): result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) return result def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' - pid = Popen("pidof kvm", shell=True, stdout=PIPE) + pid = Popen("pidof qemu-system-x86_64", shell=True, stdout=PIPE) return pid.communicate()[0].split() if __name__ == "__main__": diff --git a/plugins/virtualization/kvm_net b/plugins/virtualization/kvm_net index 5754a5ec..dfe2cd79 100755 --- a/plugins/virtualization/kvm_net +++ b/plugins/virtualization/kvm_net @@ -84,7 +84,7 @@ def find_vm_names(pids): result = {} for pid in pids: cmdline = open("/proc/%s/cmdline" % pid, "r") - result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_]*)\x00\-.*$",r"\1", cmdline.readline())) + result[pid] = clean_vm_name(re.sub(r"^.*-name\x00([a-zA-Z0-9.-_-]*)\x00\-.*$",r"\1", cmdline.readline())) return result def get_vm_mac(pid): @@ -100,7 +100,7 @@ def list_pids(): ''' Find the pid of kvm processes @return a list of pids from running kvm ''' - pid = Popen("pidof kvm", shell=True, stdout=PIPE) + pid = Popen("pidof qemu-system-x86_64", shell=True, stdout=PIPE) return pid.communicate()[0].split() def find_vms_tap(): diff --git a/plugins/weather/openweather_ b/plugins/weather/openweather_ index 47b1c128..bbd3c7a4 100644 --- a/plugins/weather/openweather_ +++ b/plugins/weather/openweather_ @@ -13,6 +13,11 @@ # http://api.openweathermap.org/data//weather? # +## From Oct 9 2015 OpenWeather needs you to register and get an APIKEY +# include this key by setting 'env.apikey' in munin plugin config, i.e.: +# [openweather_*] +# env.apikey XYZ + query_string=$(printf '%s' "${0#*_}" | tr '_' '=') TMPFILE=$(mktemp) trap 'rm -f $TMPFILE' EXIT @@ -20,7 +25,7 @@ trap 'rm -f $TMPFILE' EXIT # API returns temp in K, we have to convert it in C KELVIN_BIAS=273 -curl -s "http://api.openweathermap.org/data/2.5/weather?mode=xml&${query_string}" > $TMPFILE +curl -s "http://api.openweathermap.org/data/2.5/weather?mode=xml&${query_string}&APPID=${apikey}" > $TMPFILE CITY=$(fgrep " [ 'python3', '-m', 'py_compile', $file ], + description => 'python3 compile', + filename => $filename + } + ); + } elsif ( $interpreter =~ m{python} ) { run_check( { command => [ 'python', '-m', 'py_compile', $file ],