Munin

De WikiMar
Dreceres ràpides: navegació, cerca

Install Munin to the Ubuntu server

sudo apt-get install munin


Dynamic graphics

To be able to zoom in the graphics, edit /etc/munin/munin.conf and change:

html_strategy cgi
graph_strategy cgi

Nginx webserver

If using Nginx, add to your host config (ex. /etc/nginx/sites-enabled/default ):

        location /munin/static/ {
                alias /etc/munin/static/;
                expires modified +1w;
        }

#       location /munin {
#               # auth_basic            "Restricted";
#               # Create the htpasswd file with the htpasswd tool.
#               # auth_basic_user_file  /etc/nginx/htpasswd;
#
#               alias /var/cache/munin/www;
#               expires modified +310s;
#       }

        location /munin/ {
                fastcgi_split_path_info ^(/munin)(.*);
                fastcgi_param PATH_INFO $fastcgi_path_info;
                fastcgi_pass unix:/var/run/munin/fastcgi-html.sock;
                include fastcgi_params;
        }

        location ^~ /munin-cgi/munin-cgi-graph/ {
                fastcgi_split_path_info ^(/munin-cgi/munin-cgi-graph)(.*);
                fastcgi_param PATH_INFO $fastcgi_path_info;
                fastcgi_pass unix:/var/run/munin/fastcgi-graph.sock;
                include fastcgi_params;
        }


Create a new service file:

cat /etc/init.d/munin-fcgi-marti 
#!/bin/bash

### BEGIN INIT INFO
# Provides:          munin-fcgi
# Required-Start:    $remote_fs $syslog $network
# Required-Stop:     $remote_fs $syslog $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start munin FCGI processes at boot time
# Description:       Start the FCGI processes behind http://munin.*/
### END INIT INFO

graph_pidfile="/var/run/munin/fcgi_graph.pid"
# Ubuntu 12.10: /usr/lib/cgi-bin/munin-cgi-graph
graph_cgi="/usr/lib/munin/cgi/munin-cgi-graph"
html_pidfile="/var/run/munin/fcgi_html.pid"
# Ubuntu 12.10: /usr/lib/cgi-bin/munin-cgi-html
html_cgi="/usr/lib/munin/cgi/munin-cgi-html"

retval=0

. /lib/lsb/init-functions

start() {
  chown munin:munin /var/log/munin/*
  echo -n "Starting munin graph FastCGI: "
  start_daemon -p ${graph_pidfile} /usr/bin/spawn-fcgi -u munin -g munin \
      -s /var/run/munin/fastcgi-graph.sock -U www-data ${graph_cgi}
  echo
  echo -n "Starting munin html FastCGI: "
  start_daemon -p ${html_pidfile} /usr/bin/spawn-fcgi -u munin -g munin \
      -s /var/run/munin/fastcgi-html.sock -U www-data ${html_cgi}
  echo
  retval=$?
}
stop() {
  echo -n "Stopping munin graph FastCGI: "
  killproc -p ${graph_pidfile} ${graph_cgi} -QUIT
  echo
  echo -n "Stopping munin html FastCGI: "
  killproc -p ${html_pidfile} ${html_cgi} -QUIT
  echo
  retval=$?
  rm /var/run/munin/fastcgi-graph.sock
  rm /var/run/munin/fastcgi-html.sock
}

case "$1" in
  start)
    start
  ;;
  stop)
    stop
  ;;
  restart)
    stop
    start
  ;;
  *)
    echo "Usage: munin-fcgi {start|stop|restart}"
    exit 1
  ;;
esac
exit $retval


Start it:

service  munin-fcgi-marti restart

Add it to autostart:

update-rc.d munin-fcgi-marti defaults



Install Munin to the Fedora/Centos server

yum install munin munin-nginx


Nginx_webserver

Same steps for the Dynamic graphics as in the Ubuntu server

Same steps for Nginx as in the Ubuntu server Munin#Nginx_webserver


Create a new service file: /etc/init.d/munin-fcgi-marti2

#!/bin/sh
### BEGIN INIT INFO
# Provides:          munin-fcgi-marti2
# Required-Start:    $remote_fs $network
# Required-Stop:     $remote_fs $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts munin-fastcgi
# Description:       Spawn Munin FCGI sockets for Web access
### END INIT INFO

#
# munin-fastcgi     Startup script for Munin CGI services
#
# chkconfig: - 84 15
# description: Loading Munin CGI services using spawn-cgi
#              HTML files and CGI.
#
# Author:  Ryan Norbauer 
# Modified:     Geoffrey Grosenbach http://topfunky.com
# Modified:     David Krmpotic http://davidhq.com
# Modified:     Kun Xi http://kunxi.org
# Modified:     http://drumcoder.co.uk/
# Modified:     http://uname.pingveno.net/
# Modified:     the_architecht http://iwbyt.com/
PATH=/usr/local/bin/:/usr/local/sbin:$PATH
DAEMON=$(which spawn-fcgi)
FCGI_GRAPH_SOCK=/var/run/munin/fcgi-graph.sock
FCGI_HTML_SOCK=/var/run/munin/fcgi-html.sock
WWW_USER=nginx
FCGI_USER=munin
FCGI_GROUP=munin
FCGI_SPAWN_GRAPH=/var/www/cgi-bin/munin-cgi-graph
FCGI_SPAWN_HTML=/var/www/cgi-bin/munin-cgi-html
PIDFILE_GRAPH=/var/run/munin/fastcgi-munin-graph.pid
PIDFILE_HTML=/var/run/munin/fastcgi-munin-html.pid
DESC="Munin FCGI for Graph and HTML"

# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
test -x $FCGI_SPAWN_GRAPH || exit 0
test -x $FCGI_SPAWN_HTML || exit 0

start() {
  $DAEMON -s $FCGI_GRAPH_SOCK -U $WWW_USER -u $FCGI_USER -g $FCGI_GROUP -P $PIDFILE_GRAPH $FCGI_SPAWN_GRAPH 2> /dev/null || echo "Graph Already running"
  $DAEMON -s $FCGI_HTML_SOCK  -U $WWW_USER -u $FCGI_USER -g $FCGI_GROUP -P $PIDFILE_HTML $FCGI_SPAWN_HTML 2> /dev/null || echo "HTML Already running"
}

stop() {
  kill -QUIT `cat $PIDFILE_GRAPH` || echo "Graph not running"
  kill -QUIT `cat $PIDFILE_HTML` || echo "HTML Not running"
}

restart() {
  kill -HUP `cat $PIDFILE_GRAPH` || echo "Can't reload Graph"
  kill -HUP `cat $PIDFILE_HTML` || echo "Can't reload HTML"
}

case "$1" in
  start)
    echo "Starting $DESC: "
    start
  ;;
  stop)
    echo "Stopping $DESC: "
    stop
  ;;
  restart|reload)
    echo "Restarting $DESC: "
    stop
    # One second might not be time enough for a daemon to stop,
    # if this happens, d_start will fail (and dpkg will break if
    # the package is being upgraded). Change the timeout if needed
    # be, or change d_stop to have start-stop-daemon use --retry.
    # Notice that using --retry slows down the shutdown process somewhat.
    sleep 1
    start
  ;;
  *)
    echo "Usage: $SCRIPTNAME {start|stop|restart|reload}" >&2
    exit 3
  ;;
esac

exit $?



Start it:

service  munin-fcgi-marti2 restart
or
systemctl restart munin-fcgi-marti2


Add it to autostart:

systemctl enable munin-fcgi-marti2

Install Munin to a client

First you need to install munin client package using the following commands

   sudo apt-get install munin-node

Now you need to edit the munin-node.conf file to specify that your monitoring server is allowed to poll the client for information.

   sudo vi /etc/munin/munin-node.conf

Search for the section that has the line "allow ^127\.0\.0\.1$". Modify the IP address to reflect your monitoring server's IP address.If your server ip is 172.30.2.100

   allow ^\.172\.30\.2\.100$

In case you use a reverse tunnel to connect, you can limit the munin-node to listen only to localhost:

#host *
#by mar: posat el host 127.0.0.1 enlloc de * ja que es connecta a traves del tunel invers
host 127.0.0.1

You probably want to cahnge the port too.

Save and exit the file

You need to restart the munin client using the following information

   sudo service munin-node restart

Now you need to login in to your munin server and edit the munin.conf file

   sudo vi /etc/munin/munin.conf

Copy the following section and change the ip address to your remote server client ip address

   [MuninMonitor]
   address 127.0.0.1
   use_node_name yes

to

   [MuninMonitor]
   address 172.30.2.101
   use_node_name yes

Finall you need to restart the apache server using the following command

   sudo service apache2 restart


Add variables

/usr/sbin/munin-node-configure --suggest
/usr/sbin/munin-node-configure --shell | sh
/etc/init.d/munin-node restart


Additional Plugins

The munin-plugins-extra package contains performance checks additional services such as DNS, DHCP, Samba, etc. To install the package run the following command from the terminal

   sudo apt-get install munin-plugins-extra

Make sure you have install this package on both the server and node machines.


Info from: http://www.ubuntugeek.com/install-munin-monitoring-tool-on-ubuntu-14-04-server.html

Other plugins added manually

Add SNMP Routers and Printers

dd-wrt allows SNMP to be enabled under the Services menu.

If you're connecting using OpenVPN you need to open the UPD port 161 in the firewall (Administration tab / firewall commands):

iptables -I INPUT 1 -p udp --dport 161 -j ACCEPT 

Then you add it to one machine working as munin-node:

munin-node-configure --shell --snmp 192.168.100.250
munin-node-configure --shell --snmp 192.168.100.250 |/bin/sh


More info: http://munin-monitoring.org/wiki/Using_SNMP_plugins

multi_tcp_ping - TCP ping plugin

Improved but originally downloaded from: http://gallery.munin-monitoring.org/contrib/plugins/network/multi_tcp_ping or http://munin-monitoring.org/browser/munin-contrib/plugins/network/multi_tcp_ping


/etc/munin/plugins/multi_tcp_ping

#!/usr/bin/perl

=head1 NAME

multi_tcp_ping - Graphs together the TCP ping results for several hosts

=head1 SYNOPSIS

This plugin is meant to be called from Munin. You should set the
'hosts' environment variable from Munin's configuration (i.e.
/etc/munin/munin.conf) to specify which hosts and ports to query.

=head1 DESCRIPTION

This plugin expects to receive the following environment variables:

=over 4

=item hosts (REQUIRED!)

Comma-separated list of hosts to query. You can specify the TCP port
to connect to on each of the hosts by listing them as host:port - The
port defaults to 80. The following is a valid hosts declaration:

    hosts='192.168.0.15, 192.168.0.18:22'

It will query host 192.168.0.15 on the default port (80), as well as
host 192.168.0.18 on port 22.

=back

If the connection was opened successfully, it gives as the return
value the time it took to establish the connection. If the requested
host is not reachable, a hard-wired '-0.01' will be returned. Why
-0.01? Because giving a negative value is the best way to easily get
-visually- that something failed. Connection establishment times are
usually in the 5-500ms range. 100ms will be not too little (and thus
invisible), not too much (and thus killing the details in our graphs).

=head1 DEPENDS ON

L<Net::Ping>

=head1 SEE ALSO

L<munin>, L<munin-node>

=head1 AUTHOR

Gunnar Wolf <[email protected]>

=head1 COPYRIGHT


Copyright 2008 Gunnar Wolf, Instituto de Investigaciones
Economicas, UNAM. This plugin 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, or any later version (at your choice).

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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

=cut

use strict;
use warnings;

# This evil "eval" is to make Travis CI able to test the plugin syntax
# without having a perl built with threads.
#
# Also: The use of interpreter-based threads in perl is officially
# discouraged.
eval 'use threads; 1;' or die 'Could not use threads';

use Net::Ping;
my (%defaults, @hosts, @hostscritic, @hostsnotcritic, $cmd_arg);

%defaults = (port => 80, timeout => 2, unreachable => -0.1);
@hostscritic = get_hosts($ENV{hosts});
@hostsnotcritic = get_hosts($ENV{hostsnotcritic});

# marti 
@hosts = (@hostscritic, @hostsnotcritic);

#ordenem pel camp etiqueta
#  @hosts = sort { host_etiqueta_for($a) cmp host_etiqueta_for($b) } @hosts;


die "Hosts not set - cannot continue\n" unless @hosts;

$cmd_arg = $ARGV[0] || '';
config() if($cmd_arg eq "config");
autoconf() if ($cmd_arg eq 'autoconf');

for my $host (@hosts) { 
    threads->new(\&ping_host, $host)
}

map {$_->join} threads->list;
exit 0;

sub ping_host {
    my ($host, $addr, $p, $ret, $time, $ip);
    $host = shift;
    $addr = host_label_for($host);

    $p=Net::Ping->new("tcp", $defaults{timeout});
    $p->hires();
    my $port = $host->[1] || $defaults{port};
    $p->{port_num} = $port;

    $p->service_check('1');
    $p->port_number($port);

    ($ret, $time, $ip) = $p->ping($host->[0]);

    $time = $defaults{unreachable} if !$ret;
    print "${addr}.value $time\n";

}

sub get_hosts {
    # Hosts are defined in the 'hosts' environment variable. It's a list of
    # hosts (and optionally ports) - We parse the list and arrange it neatly
    # to be easily consumed.
    my ($hostsdef, @hosts);
    $hostsdef = shift;
    return unless $hostsdef;

    for my $host (split(/,/, $hostsdef)) {
	# $host =~ s/\s//g;

	$host =~ /^(?:([^:]+))
	    (?::(\d+))? (?::([^:,]+))?$/x;
        #$host =~ /^(?:([^:]+))
        #    (?::(\d+))?$/x;

        my $martinom = $1;
	my $martiport = $2;
	my $martietiqueta = $3;
        $martinom =~ s/\s//g;
        $martiport =~ s/\s//g;


	push @hosts, [$martinom, $martiport || $defaults{port}, $martietiqueta || "$martinom $martiport"];

    }

    return @hosts;
}

sub config {
    my @res = ("graph_title TCP connection times",
	       "graph_args --base 1000 --units-exponent -3 --lower-limit -0.099",  # "graph_args --base 1000 -l 0" 
	       "graph_scale no",
               "graph_printf %6.6lf", # per posar tot en ms "graph_printf %6.3lf%S"
	       "graph_vlabel seconds",
	       "graph_category network",
	       "graph_info Shows the time to establish a TCP connection");
    for my $host (@hostscritic) {
	my $addr = host_label_for($host);
	my $martietiqueta = host_etiqueta_for($host);
	push @res, "$addr.label $martietiqueta";
	# push @res, "$addr.extinfo $martietiqueta";
	push @res, "$addr.draw LINE2";
	push @res, "$addr.info Time to establish TCP connection to " .
	    "$host->[0]:$host->[1]";
	push @res, "$addr.critical 0:";
		# en cas que sigu per sota de 0, es un error ja que quan falla el valor es -0.10
    }

    for my $host (@hostsnotcritic) {
        my $addr = host_label_for($host);
        my $martietiqueta = host_etiqueta_for($host);
        push @res, "$addr.label $martietiqueta";
        # push @res, "$addr.extinfo $martietiqueta";
        push @res, "$addr.draw LINE2";
        push @res, "$addr.info Time to establish TCP connection to " .
             "$host->[0]:$host->[1]";
        push @res, "$addr.critical :";
                # aquest es un host no critic i per tan no ha denviar alertes
     }

    print map {"$_\n"} @res;
    exit 0;
}

sub autoconf {
    print "yes\n";
    exit 0;
}

sub host_label_for {
    my ($ip, $port, $etiqueta) = @{$_[0]};
    # Periods and colonsare not allowed in variable names
    my $addr = "src_${ip}_${port}";
    $addr =~ s/\./_/g;
    return $addr;
}

sub host_etiqueta_for {
    my ($ip, $port, $etiqueta) = @{$_[0]};
    return $etiqueta;
}





And put it to:

sudo vi /etc/munin/plugins/multi_tcp_ping
sudo chmod +x /etc/munin/plugins/multi_tcp_ping

Add the hosts to the following new file:

sudo vi /etc/munin/plugin-conf.d/multi_tcp_ping

or at the end of:

sudo vi /etc/munin/plugin-conf.d/munin-node
[multi_tcp_ping]
env.hosts 192.168.1.2, google.com, 192.168.100.1, 192.168.100.2, 192.168.100.100:3389:This is a label for this server, 192.168.101.1

env.hostsnotcritic google.com, ocell1.min.cat:23:Ocell1

# env.hostsnotcritic are hosts that no alerts have to be sent if unreachable


Restart munin-node

service munin-node restart


Old system with a dedicated hosts file

(the ports specified in the file do not work yet, so only 80 is working)

From: https://forum.xakep.ru/topic/1398094/

Create file /usr/share/munin-marti/tcp_multi_ping'

cat tcp_multi_ping
#!/usr/bin/perl

=head1 NAME

multi_tcp_ping - Graphs together the TCP ping results for several hosts

=head1 SYNOPSIS

This plugin is meant to be called from Munin. You should set the
'hosts' environment variable from Munin's configuration (i.e.
/etc/munin/munin.conf) to specify which hosts and ports to query.

=head1 DESCRIPTION

This plugin expects to receive the following environment variables:

=over 4

=item hosts (REQUIRED!)

Comma-separated list of hosts to query. You can specify the TCP port
to connect to on each of the hosts by listing them as host:port - The
port defaults to 80. The following is a valid hosts declaration:

    hosts='192.168.0.15, 192.168.0.18:22'

It will query host 192.168.0.15 on the default port (80), as well as
host 192.168.0.18 on port 22.

=back

If the connection was opened successfully, it gives as the return
value the time it took to establish the connection. If the requested
host is not reachable, a hard-wired '-0.01' will be returned. Why
-0.01? Because giving a negative value is the best way to easily get
-visually- that something failed. Connection establishment times are
usually in the 5-500ms range. 100ms will be not too little (and thus
invisible), not too much (and thus killing the details in our graphs).

=head1 DEPENDS ON

L<Net::Ping>

=head1 SEE ALSO

L<munin>, L<munin-node>

=head1 AUTHOR

Gunnar Wolf <[email protected]>

=head1 COPYRIGHT


Copyright 2008 Gunnar Wolf, Instituto de Investigaciones
Economicas, UNAM. This plugin 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, or any later version (at your choice).

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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

=cut

use strict;
use warnings;

# This evil "eval" is to make Travis CI able to test the plugin syntax
# without having a perl built with threads.
#
# Also: The use of interpreter-based threads in perl is officially
# discouraged.
eval 'use threads; 1;' or die 'Could not use threads';

use Net::Ping;
my (%defaults, @hosts, $cmd_arg, $config_file_name);
%defaults = (port => 80, timeout => 2, unreachable => -0.01);

#$config_file_name = "/usr/share/munin/plugins/hostslist.ip";
$config_file_name = "/usr/share/munin-marti/tcp_multi_ping.ip";



open (IN_FILE,"<$config_file_name") or die "Unable to open file hostslist.ip";
while (<IN_FILE>){
# if (/\d+\.\d+\.\d+\.\d+/){
s/\s//g; push (@hosts,[$_,$defaults{port}]);
# }
}

# @hosts = get_hosts($ENV{hosts});


die "Hosts not set - cannot continue\n" unless @hosts;

$cmd_arg = $ARGV[0] || '';
config() if($cmd_arg eq "config");
autoconf() if ($cmd_arg eq 'autoconf');

for my $host (@hosts) { 
    threads->new(\&ping_host, $host)
}

map {$_->join} threads->list;
exit 0;

sub ping_host {
    my ($host, $addr, $p, $ret, $time, $ip);
    $host = shift;
    $addr = host_label_for($host);

    $p=Net::Ping->new("tcp", $defaults{timeout});
    $p->hires();
    $p->{port_num} = $host->[1] || $defaults{port};

    ($ret, $time, $ip) = $p->ping($host->[0]);

    $time = $defaults{unreachable} if !$ret;
    print "${addr}.value $time\n";
}

sub get_hosts {
    # Hosts are defined in the 'hosts' environment variable. It's a list of
    # hosts (and optionally ports) - We parse the list and arrange it neatly
    # to be easily consumed.
    my ($hostsdef, @hosts);
    $hostsdef = shift;
    return unless $hostsdef;

    for my $host (split(/,/, $hostsdef)) {
        $host =~ s/\s//g;

        $host =~ /^(?:([^:]+))
            (?::(\d+))?$/x;

        push @hosts, [$1, $2 || $defaults{port}];

    }

    return @hosts;
}

sub config {
    my @res = ("graph_title TCP connection times",
               "graph_args --base 1000 -l 0",
               "graph_vlabel seconds",
               "graph_category network",
               "graph_info Shows the time to establish a TCP connection");
    for my $host (@hosts) {
        my $addr = host_label_for($host);
        push @res, "$addr.label $addr";
        push @res, "$addr.draw LINE2";
        push @res, "$addr.info Time to establish TCP connection to " .
            "$host->[0]:$host->[1]";

    }

    print map {"$_\n"} @res;
    exit 0;
}

sub autoconf {
    print "yes\n";
    exit 0;
}

sub host_label_for {
    my ($ip, $port) = @{$_[0]};
    # Periods and colonsare not allowed in variable names
    my $addr = "src_${ip}_${port}";
    $addr =~ s/\./_/g;
    return $addr;
}


Put all your hosts to /usr/share/munin-marti/tcp_multi_ping.ip

google.com
hostssh.com:22

Create link:

sudo ln -s '/usr/share/munin-marti/tcp_multi_ping' '/etc/munin/plugins/tcp_multi_ping'


Restart munin-node

multi_backup_times

/usr/share/munin-marti/multi_backup_times

#!/usr/bin/perl

=head1 NAME

multi_time_backup - Time since last succesful backup for several backup systems

=head1 SYNOPSIS

This plugin is meant to be called from Munin.

=head1 DESCRIPTION

This plugin expects to receive the following config :

=over 4

=commands file (REQUIRED!)

File with each line containing 4 fields separated by : in the following format
dayswarn:dayscrit:id:label:command

dayswarn is maximun amount of days before warning alert (empty for no alert)
dayscrit is maximun amount of days before critical alert (empty for no alert)
id is a unique id used to store the data
label is the label displayed (if empty then the id is going to be used)
command is the command to run in bash


=back

If the command to run is executed correctly it should return the number of seconds
since the last backup.
In case of error it should return -1

=head1 SEE ALSO

L<munin>, L<munin-node>

=head1 AUTHOR

M Minoves <info at sitoplex . com>

=head1 COPYRIGHT


Copyright 2017    M Minoves <info at sitoplex . com>
This plugin 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, or any later version (at your choice).

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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

=cut

use strict;
use warnings;

# This evil "eval" is to make Travis CI able to test the plugin syntax
# without having a perl built with threads.
#
# Also: The use of interpreter-based threads in perl is officially
# discouraged.
eval 'use threads; 1;' or die 'Could not use threads';

use Net::Ping;
my (%defaults, @backups, $cmd_arg, $config_file_name);
# %defaults = (diesmax => 10);


$config_file_name = "/usr/share/munin-marti/multi_backup_times.list";

open (IN_FILE,"<$config_file_name") or die "Unable to open file " . $config_file_name;

my ($linia, $diesmaxwarning, $diesmaxcritical, $id, $etiqueta, $commanda);
while (<IN_FILE>){

        $linia = $_;
        if($linia =~ /^([^#:]*):([^:]+):([^:]+):([^:]*):(.{3,})$/x)
        {
        $diesmaxwarning = $1;
        $diesmaxcritical = $2;
            $id = $3;
            $etiqueta = $4 || $3;
            $commanda = $5;

        $diesmaxwarning =~ s/[^0-9.]//g;
        $diesmaxcritical =~ s/[^0-9.]//g;
        $id =~ s/\s/_/g;
        $id =~ s/\./_/g;
        $id =~ s/[^0-9a-zA-Z_\-]//g;

        if (length ($commanda) > 3)
        {    push @backups, [$diesmaxwarning, $diesmaxcritical, $id, $etiqueta, $commanda];
        }
    }
}

die "$config_file_name file not correctly set - cannot continue\n" unless @backups;

$cmd_arg = $ARGV[0] || '';
config() if($cmd_arg eq "config");
autoconf() if ($cmd_arg eq 'autoconf');

for my $backup (@backups) {
    threads->new(\&exectuarCommanda, $backup)
}

# esperem a que tots els threads acabin:
map {$_->join} threads->list;
exit 0;

sub exectuarCommanda {
        my ($backup, $addr);
        $backup = shift;
        $addr = get_label_for($backup);

        my $commanda = get_commanda_for($backup);


        # my $time = `/bin/bash -c '$commanda'`;   # or die "Cannot open Dir: $!";
        my $time = `$commanda`;   # or die "Cannot open Dir: $!";
        if ( $? != 0 )
        {
           # print "command failed: $!\n";
           # printf "command exited with value %d\n", $? >> 8;
           $time = "-1";
        }
        else
        {
            $time =  $time/(60*60*24);  # convertim de segons a dies
        }

        print "${addr}.value $time\n";
}


sub config {
    my @res = ("graph_title Times since last backups",
               "graph_args --base 1000 --units-exponent 0 -l 0",
                           "graph_scale no",
                           "graph_printf %6.2lf",
               "graph_vlabel days",
               "graph_category security",
               "graph_info Shows the time since the last successful backups");

    for my $backup (@backups) {
        my $addr = get_label_for($backup);
        my $martietiqueta = get_etiqueta_for($backup);
        push @res, "$addr.label $martietiqueta";
        # push @res, "$addr.extinfo Command:  " . get_commanda_for($backup);
        push @res, "$addr.draw LINE2";
        push @res, "$addr.info " . get_commanda_for($backup);
        push @res, "$addr.warning 0:" . get_diesmaxwarning_for($backup);
        push @res, "$addr.critical 0:" . get_diesmaxcritical_for($backup);
    }

    print map {"$_\n"} @res;
    exit 0;
}

sub autoconf {
    print "yes\n";
    exit 0;
}

sub get_label_for {
    my ($diesmaxwarning, $diesmaxcritical, $id, $etiqueta, $commanda) = @{$_[0]};
    # Periods and colons are not allowed in variable names
    my $label = "src_${id}";
    # $label =~ s/ /_/g;
    # $label =~ s/\./_/g;
    return $label;
}

sub get_etiqueta_for {
    my ($diesmaxwarning, $diesmaxcritical, $id, $etiqueta, $commanda) = @{$_[0]};
    return $etiqueta;
}

sub get_commanda_for {
    my ($diesmaxwarning, $diesmaxcritical, $id, $etiqueta, $commanda) = @{$_[0]};
    return $commanda;
}

sub get_diesmaxwarning_for {
    my ($diesmaxwarning, $diesmaxcritical, $id, $etiqueta, $commanda)  = @{$_[0]};
    return $diesmaxwarning;
}

sub get_diesmaxcritical_for {
    my ($diesmaxwarning, $diesmaxcritical, $id, $etiqueta, $commanda) = @{$_[0]};
    return $diesmaxcritical;
}


/usr/share/munin-marti/multi_backup_times.list

8:15:subcube3_to_subcube2_etc:Subcube3 to Subcube2 web backup:f=$(ls -tr -d -1 /seguretat/subcube3/basedades/*.* | tail -n 1); [ $(du -m "$f" | cut -f 1) -ge 100 ] && echo $(( ($(date +%s) - $(date -r "$f" +%s)) ))
8:15:subcube3_to_subcube2_web:Subcube3 to Subcube2 web backup:f=/var/log/backup-subcube3-subcube2-web-success.log; [ $(du -b "$f" | cut -f 1) -ge 10 ] && echo $(( ($(date +%s) - $(date --date="$(tail -n 1 $f)" +%s)) ))


Apt Ubuntu

sudo vi /etc/munin/plugins/apt_ubuntu

From: https://raw.githubusercontent.com/munin-monitoring/contrib/master/plugins/ubuntu/apt_ubuntu

#!/usr/bin/env python
# -*- encoding: iso-8859-1 -*-
#
# apt_ubuntu
#
# Plugin to monitor packages that should be installed on Ubuntu systems.
#
# Author: Stefan Daniel Schwarz <[email protected]>
#
# v1.0 2008-11-07 - First draft
# v1.1 2008-11-08 - critical = #: First # critical, rest warning
# v1.2 2008-11-09 - Code cleanup for MuninExchange submission
#
# Usage: place in /etc/munin/plugins/ (or link it there using ln -s)
#
# Parameters understood:
#
#       config   (required)
#       autoconf (optional - used by munin-config)
#
# Magic markers - optional - used by installation scripts and
# munin-config:
#
#%# capabilities=autoconf
#%# family=contrib

###########################################################
category = 'security' # 'upgrades'
title = 'Upgradable packages' # 'Upgradeable packages'
vlabel = 'Total packages'
other = 'other'
total = 'total'

archives = ['security', 'updates', 'proposed', 'backports']
colour = ['ff0000', '22ff22', '0022ff', '00aaaa', 'ff00ff']
origins = ['Ubuntu']

critical = 1
###########################################################

import os
import sys
import warnings

warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning)

def autoconf():
    if os.path.exists('/etc/lsb-release'):
        for line in open('/etc/lsb-release'):
            if line.strip() == 'DISTRIB_ID=Ubuntu':
                try:
                    import apt
                except ImportError:
                    print 'no (python-apt not installed)'
                    sys.exit(1)
                cache = apt.Cache()
                if not cache.has_key('update-notifier-common'):
                    print 'no (update-notifier-common not found)'
                    sys.exit(1)
                if not cache['update-notifier-common'].isInstalled:
                    print 'no (update-notifier-common not installed)'
                    sys.exit(1)
                if not os.path.exists('/etc/apt/apt.conf.d/10periodic'):
                    print 'no (/etc/apt/apt.conf.d/10periodic not found)'
                    sys.exit(1)
                for line in open('/etc/apt/apt.conf.d/10periodic'):
                    if line.strip() == 'APT::Periodic::Update-Package-Lists "1";':
                        print 'yes'
                        sys.exit(0)
                print 'no (APT::Periodic::Update-Package-Lists not "1")'
                sys.exit(1)
    print 'no'
    sys.exit(1)

def config():
    print 'graph_category security'
    print 'graph_title %s' % (title)
    #print 'graph_total %s' % (total)
    print 'graph_vlabel %s' % (vlabel)
    for i, archive in enumerate(archives + [other]):
        if len(colour) > i:
            print '%s.colour %s' % (archive, colour[i])
        if i < critical:
            print '%s.critical 0:0' % (archive)
        if i == 0:
            print '%s.draw AREA' % (archive)
        else:
            print '%s.draw STACK' % (archive)
        print '%s.label %s' % (archive, archive)
        if i + 1 > critical:
            print '%s.warning 0:0' % (archive)
    print 'total.colour 000000'
    print 'total.draw LINE1'
    print 'total.label %s' % (total)
    sys.exit(0)

def check_origin(pkg):
    #print 'Checking: %s (%s)' % (pkg.name, map(str, pkg.candidateOrigin))
    if pkg.candidate.origins:
        for archive in archives:
            for origin in pkg.candidate.origins:
                #a = origin.archive.rpartition('-')[2]
                a = origin.archive.split('-')[origin.archive.count('-')]
                if a == archive and origin.origin in origins:
                    return a
    return other

if len(sys.argv) > 1:
    if sys.argv[1] == 'autoconf':
        autoconf()
    elif sys.argv[1] == 'config':
        config()
    elif sys.argv[1]:
        print('unknown argument "' + sys.argv[1] + '"')
        sys.exit(1)

try:
    import apt
except ImportError:
    print "The module 'apt' is currently not installed.  You can install it by typing:\nsudo apt-get install python-apt\nImportError: No module named apt"
    sys.exit(1)

pkgs = {}
total = 0
for pkg in apt.Cache():
    if pkg.is_upgradable:
        a = check_origin(pkg)
        pkgs[a] = pkgs.get(a, 0) + 1
        total += 1

for archive in archives + [other]:
    print '%s.value %s' % (archive, pkgs.pop(archive, 0))

print 'total.value %s' % (total)

chmod +x /etc/munin/plugins/apt_ubuntu

Restart munin-node


Yum Fedora Centos RHEL

Originally from https://www.potsky.com/yum-plugin-for-munin/ but upgraded to also list the Security packages and not requiring an extra cronjob.

/usr/share/munin-marti/yum

#!/usr/bin/perl -w
# -*- perl -*-
# vim: ft=perl : sw=4 : ts=4 : et

=head1 NAME

yum - Plugin for monitoring pending package upgrades with yum

=head1 USAGE

This plugin needs to be called with the 'update' argument
from cron to work as intended.

=head1 AUTHOR

Copyright 2006 Dagfinn Ilmari Mannsåker <[email protected]>
Copyright 2011 Adds by potsky <[email protected]>
Copyright 2017 Adds by M M <info at sitoplex .com>


=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

 #%# family=auto
 #%# capabilities=autoconf

=cut

#use strict;
#use Munin::Common::Defaults;
#
#my $statefile = "$Munin::Common::Defaults::MUNIN_PLUGSTATE/yum.state";
#
#sub update {
#    if (-l $statefile) {
#    die "$statefile is a symlink, not touching.\n";
#    }
#
#    open my $state, '>', $statefile
#    or die "Can't open $statefile for writing: $!\n";
#
#    open my $yum, '-|', 'yum list updates'
#    or die "Can't run 'yum list updates': $!";
#
#    # Skip header crap
#    while (<$yum>) {
#    last if /^Updated/;
#    }
#
#    while (<$yum>) {
#    next unless /^(\S+)\.\S+\s+\S+\s+\S+/;
#    print $state "$1\n";
#    }
#
#    close $yum or die "Error running 'yum list updates': $!\n";
#    close $state or die "Error writing $statefile: $!\n";
#}

sub autoconf {
    if (system('yum --version >/dev/null 2>/dev/null') != 0) {
        print "no (Could not run yum)\n";
    }
#    elsif (! -r $statefile) {
#        print "no (Could not find statefile.  Please read 'munindoc yum')\n";
#    }
    else {
        print "yes\n";
    }
    exit 0;
}

sub config {
    print "graph_title Upgradable packages in yum\n";
    print "graph_category security\n";
    print "graph_vlabel Total packages\n";
    print "graph yes\n";
    print "updates.label updates\n";
    print "updates.warning 0:0\n";
    print "updates.colour 22ff22\n";
    print "updates.draw AREA\n";
    print "security.label security\n";
    print "security.critical 0:0\n";
    print "security.colour ff0000\n";
    print "security.draw AREA\n";

}

sub report {

#   my @packages;
#
#   open my $state, '<', $statefile
#   or die "Can't open $statefile for reading: $!
#Please read 'munindoc yum' to understand why if the file does not exist.\n";
#
#   chomp(@packages = <$state>);
#   close $state;


   $outputyum = `yum  --security check-update |tail -n1`;
   #chomp $outputyum;
   if($outputyum =~ /^(\d+) package\(s\) needed for security, out of (\d+) available$/)  # rhel6
   {
        print 'updates.value ', $2, "\n";
        print 'security.value ', $1, "\n";
   }elsif($outputyum =~ /^No packages needed for security; (\d+) packages available$/)  # rhel6
   {
        print 'updates.value ', $1, "\n";
        print 'security.value 0', "\n";

   }elsif($outputyum =~ /^Needed (\d+) of (\d+) packages, for security$/)  # rhel5
   {
        print 'updates.value ', $2, "\n";
        print 'security.value ', $1, "\n";
   }elsif($outputyum =~ /^No packages needed, for security, (\d+) available$/)  # rhel5
   {
        print 'updates.value ', $1, "\n";
        print 'security.value 0', "\n";

   }else
   {
        print 'Error parsing: "' . $outputyum . '"' ;
        print 'updates.value -1', "\n";
        print 'security.value -1', "\n";

   }


#   print 'pending.value ', scalar(@packages), "\n";
#   print 'pending.extinfo ', join(' ', @packages), "\n"
#   if @packages;
}

if ($ARGV[0]) {
    my $arg = $ARGV[0];
    my %funcs = (
#        update   => \&update,
        config   => \&config,
        autoconf => \&autoconf,
    );

    if (exists $funcs{$arg}) {
    $funcs{$arg}->();
    } else {
    die "Unknown argument '$arg'\n";
    }
} else {
    report();
}

multi_icmp_ping

/etc/munin/plugin-conf.d/multi_icmp_ping

[multi_icmp_ping]
user root
env.hosts 10.0.0.15, 10.0.0.16


/etc/munin/plugins/multi_icmp_ping

#!/usr/bin/perl

=head1 NAME

multi_icmp_ping - Graphs together the ICMP ping results for several hosts

=head1 SYNOPSIS

This plugin is meant to be called from Munin. You should set the
'hosts' environment variable from Munin's configuration (i.e.
/etc/munin/munin.conf) to specify which hosts and ports to query.

=head1 DESCRIPTION

This plugin expects to receive the following environment variables:

=over 4

=item hosts (REQUIRED!)

Comma-separated list of hosts to query. The following is a valid hosts declaration:

    hosts='192.168.0.15, 192.168.0.18'


=back

If the connection was opened successfully, it gives as the return
value the time it took to establish the connection. If the requested
host is not reachable, a hard-wired '-0.01' will be returned. Why
-0.01? Because giving a negative value is the best way to easily get
-visually- that something failed. Connection establishment times are
usually in the 5-500ms range. 100ms will be not too little (and thus
invisible), not too much (and thus killing the details in our graphs).

=head1 DEPENDS ON

L<Net::Ping>

=head1 SEE ALSO

L<munin>, L<munin-node>

=head1 AUTHOR

M Minoves

=head1 COPYRIGHT


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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

=cut

use strict;
use warnings;

# This evil "eval" is to make Travis CI able to test the plugin syntax
# without having a perl built with threads.
#
# Also: The use of interpreter-based threads in perl is officially
# discouraged.
eval 'use threads; 1;' or die 'Could not use threads';

use Net::Ping;
my (%defaults, @hosts, $cmd_arg);

%defaults = (port => 80, timeout => 2, unreachable => -0.01);
@hosts = get_hosts($ENV{hosts});
die "Hosts not set - cannot continue\n" unless @hosts;

$cmd_arg = $ARGV[0] || '';
config() if($cmd_arg eq "config");
autoconf() if ($cmd_arg eq 'autoconf');

for my $host (@hosts) { 
    threads->new(\&ping_host, $host)
}

map {$_->join} threads->list;
exit 0;

sub ping_host {
    my ($host, $addr, $p, $ret, $time, $ip);
    $host = shift;
    $addr = host_label_for($host);

    $p=Net::Ping->new("icmp", $defaults{timeout});
    $p->hires();
    #$p->{port_num} = $host->[1] || $defaults{port};

    ($ret, $time, $ip) = $p->ping($host->[0]);

    $time = $defaults{unreachable} if !$ret;
    print "${addr}.value $time\n";
}

sub get_hosts {
    # Hosts are defined in the 'hosts' environment variable. It's a list of
    # hosts (and optionally ports) - We parse the list and arrange it neatly
    # to be easily consumed.
    my ($hostsdef, @hosts);
    $hostsdef = shift;
    return unless $hostsdef;

    for my $host (split(/,/, $hostsdef)) {
	$host =~ s/\s//g;

	$host =~ /^(?:([^:]+))
	    (?::(\d+))?$/x;

	push @hosts, [$1, $2 || $defaults{port}];

    }

    return @hosts;
}

sub config {
    my @res = ("graph_title ICMP connection times",
	       "graph_args --base 1000 -l 0",
	       "graph_vlabel seconds",
	       "graph_category network",
	       "graph_info Shows the time to respond to an ICMP ping");
    for my $host (@hosts) {
	my $addr = host_label_for($host);
	push @res, "$addr.label $addr";
	push @res, "$addr.draw LINE2";
	push @res, "$addr.info Time to respond to an ICMP ping to " .
	    "$host->[0]:$host->[1]";

    }

    print map {"$_\n"} @res;
    exit 0;
}

sub autoconf {
    print "yes\n";
    exit 0;
}

sub host_label_for {
    my ($ip, $port) = @{$_[0]};
    # Periods and colonsare not allowed in variable names
    #my $addr = "src_${ip}_${port}";
    my $addr = "src_${ip}";
    $addr =~ s/\./_/g;
    return $addr;
}



multi_ssh_ping

/etc/munin/plugin-conf.d/multi_ssh_ping

[multi_ssh_ping]
env.hosts [email protected]:9.9.9.9, [email protected]:9.9.9.10
env.sshcommand sshpass -p password ssh -o StrictHostKeyChecking=no
env.pingcommand ping -A -w 2 -c 5


/etc/munin/plugins/multi_ssh_ping

#!/usr/bin/perl

=head1 NAME

multi_ssh_ping - Graphs together the pings mesured from a another host connecting using SSH

=head1 SYNOPSIS

This plugin is meant to be called from Munin. You should set the
'hosts' environment variable from Munin's configuration (i.e.
/etc/munin/munin.conf) to specify which hosts and ports to query.

=head1 DESCRIPTION

This plugin expects to receive the following environment variables:

=over 4

=item hosts (REQUIRED!)

Comma-separated list of sshhost:pinghosts to query. It connects to sshhost using ssh and from there it pings pinghost. The following is a valid hosts declaration:

	hosts='[email protected]:192.168.0.17, [email protected]:192.168.0.18'

=item sshcommand (REQUIRED!)

The ssh command to use before each of the hosts above

	sshcommand='sshpass -p password ssh -o StrictHostKeyChecking=no'

=item pingcommand (REQUIRED!)

The ping command to use after each of the hosts above

        pingcommand='ping -c 2 -w 1'


=back

If the connection was opened successfully, it gives as the return
value the time it took to establish the connection. If the requested
host is not reachable, a hard-wired '-0.01' will be returned. Why
-0.01? Because giving a negative value is the best way to easily get
-visually- that something failed. Connection establishment times are
usually in the 5-500ms range. 100ms will be not too little (and thus
invisible), not too much (and thus killing the details in our graphs).

=head1 DEPENDS ON

L<Net::Ping>

=head1 SEE ALSO

L<munin>, L<munin-node>

=head1 AUTHOR

Minoves

=head1 COPYRIGHT


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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

=cut

use strict;
use warnings;

# This evil "eval" is to make Travis CI able to test the plugin syntax
# without having a perl built with threads.
#
# Also: The use of interpreter-based threads in perl is officially
# discouraged.
eval 'use threads; 1;' or die 'Could not use threads';

#use Net::Ping;
my (%defaults, @hosts, $cmd_arg, $pingcommand, $sshcommand);

%defaults = (unreachable => -0.01);
@hosts = get_hosts($ENV{hosts});
die "hosts not set - cannot continue\n" unless @hosts;


$sshcommand = $ENV{sshcommand};
die "sshcommand not set - cannot continue\n" unless $sshcommand;

$pingcommand = $ENV{pingcommand};
die "pingcommand not set - cannot continue\n" unless $pingcommand;


$cmd_arg = $ARGV[0] || '';
config() if($cmd_arg eq "config");
autoconf() if ($cmd_arg eq 'autoconf');

for my $host (@hosts) { 
    threads->new(\&ping_host, $host)
}

map {$_->join} threads->list;
exit 0;

sub ping_host {
    my ($host, $addr, $p, $ret, $time, $ip);
    $host = shift;
    $addr = host_label_for($host);
    
        my $execcommand = $sshcommand . ' ' . $host->[0]  . ' ' . $pingcommand . ' ' . $host->[1];
	my @ping = `$execcommand`;
	chomp @ping;
	my $ping = join(" ", @ping);
	#my $ping_time = "U";
	#my $packet_loss = "U";
	
	my $ping_time = $defaults{unreachable};
	$ping_time = ($1 / 1000) if ($ping =~ m@min/avg/max.*\s\d+(?:\.\d+)?/(\d+(?:\.\d+)?)/\d+(?:\.\d+)?@);
	# $packet_loss = $1 if ($ping =~ /(\d+)% packet loss/);
	print "${addr}.value ".  $ping_time . "\n";

}

sub get_hosts {
    # Hosts are defined in the 'hosts' environment variable. 
    my ($hostsdef, @hosts);
    $hostsdef = shift;
    return unless $hostsdef;

    for my $host (split(/,/, $hostsdef)) {
	$host =~ s/\s//g;

	$host =~ /^(?:([^:]+))
	    (?::(.+))?$/x;

	push @hosts, [$1, $2];

    }

    return @hosts;
}

sub config {
    my @res = ("graph_title SSH ping delay times",
	       "graph_args --base 1000 -l 0",
	       "graph_vlabel seconds",
	       "graph_category network",
	       "graph_info Shows the time to respond to an ICMP ping from a SSH host");
    for my $host (@hosts) {
	my $addr = host_label_for($host);
	push @res, "$addr.label $addr";
	push @res, "$addr.draw LINE2";
	push @res, "$addr.info ICMP ping time from " .
	    "$host->[0]" . " to " . "$host->[1]";

    }

    print map {"$_\n"} @res;
    exit 0;
}

sub autoconf {
    print "yes\n";
    exit 0;
}

sub host_label_for {
    my ($ssh, $host) = @{$_[0]};
    # Periods and colonsare not allowed in variable names
    $ssh =~ s/.*\@//g;
    my $addr = "src_${ssh}__${host}";
    $addr =~ s/\./_/g;
    return $addr;
}