From 78a229dd0d851d2728c490cb41de8ef088ddc720 Mon Sep 17 00:00:00 2001 From: Gabriel Filion Date: Wed, 4 Feb 2015 23:54:38 -0500 Subject: [PATCH 001/718] mysql_connections_per_user: skip internal user "system user" This is the user name used internally by mysql for binlog handling (even though it's not present in the mysql.user table). The space in the name makes the plugin freak out about an unknown "system" value. Number of connections for this interal user is irrelevant. --- plugins/mysql/mysql_connections_per_user | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/mysql/mysql_connections_per_user b/plugins/mysql/mysql_connections_per_user index 9cc50908..28552d4b 100755 --- a/plugins/mysql/mysql_connections_per_user +++ b/plugins/mysql/mysql_connections_per_user @@ -91,6 +91,10 @@ sub print_graph_data() { my $print_user = ""; foreach my $user (reverse sort { $counts{$a} <=> $counts{$b} } keys %counts) { last if $i++ >= $numusers; + if ($user eq "system user") { + #skip internal user that manages binlog operations for slave servers. + next; + } $total += $counts{$user}; $print_user = $user; if($print_user eq "root") { @@ -125,6 +129,10 @@ EOM my $i = 0; foreach my $user (reverse sort { $counts{$a} <=> $counts{$b} } keys %counts) { last if $i++ >= $numusers; + if ($user eq "system user") { + #skip internal user that manages binlog operations for slave servers. + next; + } my $print_user = $user; if($print_user eq "root") { $print_user = "root_"; From 311e2aa08242fdc3a38105c544254bdf8347b10a Mon Sep 17 00:00:00 2001 From: Lee Clemens Date: Wed, 15 Apr 2015 12:29:09 -0400 Subject: [PATCH 002/718] Add graph_scale no to php_acp_ for Percents graph --- plugins/php/php_apc_ | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/php/php_apc_ b/plugins/php/php_apc_ index 3217392a..eeb76ecf 100755 --- a/plugins/php/php_apc_ +++ b/plugins/php/php_apc_ @@ -94,6 +94,7 @@ if [ "$1" = "config" ] && [ "$act" = "percents" ]; then cat <<'EOM' graph_title APC Percents graph_args -l 0 --upper-limit 100 +graph_scale no graph_vlabel Cache Percents % graph_category php-apc hits.label hits From fe2707654fe083dc3c1af75c22c8f80f24a4154d Mon Sep 17 00:00:00 2001 From: Paul Saunders Date: Mon, 20 Jul 2015 10:23:32 +0100 Subject: [PATCH 003/718] zenus_: Handle "unlimited" accounts --- plugins/network/zenus_ | 116 +++++++++++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 16 deletions(-) diff --git a/plugins/network/zenus_ b/plugins/network/zenus_ index 00e2a5b5..4d8c8ff2 100755 --- a/plugins/network/zenus_ +++ b/plugins/network/zenus_ @@ -66,6 +66,14 @@ if ( !eval "require zenus;" ) { $ret .= "(BTW, \@INC is: " . join( ', ', @INC ) . ")\n"; } +my $CAN_LOG = 1; +if ( !eval "require Log::Log4perl;" ) { + $CAN_LOG = 0; +} +elsif ( !eval "require Log::Dispatch::FileRotate;" ) { + $CAN_LOG = 0; +} + my $USER = $ENV{user}; my $PASS = $ENV{pass}; my $ACCT = $ENV{account} || ""; @@ -85,15 +93,31 @@ if ( scalar @name_fields == 3 ) { # If there are more or less than 3 components to the filename, # we just carry on with the default account +if ($CAN_LOG) { + my $loggerconf = q( + log4perl.rootLogger = DEBUG, LOG1 + log4perl.appender.LOG1 = Log::Dispatch::FileRotate + log4perl.appender.LOG1.filename = /var/log/munin/zenus.log + log4perl.appender.LOG1.mode = append + log4perl.appender.LOG1.DatePattern = yyyy-ww + log4perl.appender.LOG1.layout = Log::Log4perl::Layout::PatternLayout + log4perl.appender.LOG1.layout.ConversionPattern = %d %p %c: %m%n + ); + Log::Log4perl::init( \$loggerconf ); + our $logger = Log::Log4perl->get_logger($ACCT); +} + my $lastread; sub save_data { my $hashref = shift; + my $update_lastread = shift || 1; # Do we need to save this info - if ( time > $lastread + ( $TICK * 60 ) ) { + if ( !defined $lastread or time > $lastread + ( $TICK * 60 ) ) { - $lastread = time; + $lastread = time if $update_lastread; + print "# Updating LastRead to " . localtime($lastread) . "\n"; my @save_vector; push @save_vector, $lastread; @@ -103,6 +127,10 @@ sub save_data { push @save_vector, $_ . '¬' . $hashref->{$_}; } + $logger->info( + "Saving Data (LastRead is " . localtime($lastread) . "\n" ) + if $CAN_LOG; + #Go! save_state(@save_vector); } @@ -121,11 +149,50 @@ sub load_data { my ( $key, $value ) = split /¬/; $hashref->{$key} = $value; } + my $force_save = 0; if ( !defined $lastread or time >= ( $lastread + ( $TICK * 60 ) ) ) { # Data is stale + print "# Data is " . ( time - $lastread ) . " seconds old\n"; + $logger->info( "Data is " . ( time - $lastread ) . " seconds old\n" ) + if $CAN_LOG; - #print STDERR "REFRESHING DATA\n"; + if ( exists $hashref->{backoff} ) { + if ( time <= $hashref->{backoff} ) { + + # We're in a back-off period + print "# Back-off in effect\n"; + $logger->info("Back-off in effect\n") if $CAN_LOG; + exit 0; + } + else { + # We've just come out of a back-off period + print "# Back-off ends\n"; + $logger->info("Back-off ends\n") if $CAN_LOG; + delete $hashref->{backoff}; + } + } + elsif ( !defined $lastread ) { + + # Initial run. Carry on + print "# Initial Run\n"; + my $force_save = 1; + } + elsif ( time >= ( $lastread + ( 120 * 60 ) ) ) { + + # Data is VERY Stale. Assume a login issue and + # back-off for 24 hours. + print "# Starting Back-Off\n"; + $hashref->{backoff} = time + ( 24 * 60 * 60 ); + $logger->info( + "Backing off until " . localtime( $hashref->{backoff} ) . "\n" ) + if $CAN_LOG; + save_data( $hashref, 0 ); + exit 1; + } + + print "# REFRESHING DATA\n"; + $logger->info("Refreshing Data\n") if $CAN_LOG; my $temphash; eval { zenus::login( $USER, $PASS ); @@ -144,11 +211,19 @@ sub load_data { = zenus::estimateRemaining( $temphash->{used}, $temphash->{avail} ); $hashref = $temphash; + 1; # If zenus threw an error we won't copy the data over, # so we still use the cached data. + } or do { + print "# Zenus Error $@\n"; }; } + else { + print "# Using existing data\n"; + $logger->info("Using existing data\n") if $CAN_LOG; + } + save_data( $hashref, 1 ) if $force_save; return $hashref; } @@ -181,15 +256,12 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) { exit 1; } my $data = load_data(); - my $cap = sprintf( "%.3f", $data->{avail} ); - my $warn = sprintf( "%.3f", $cap * 0.25 ); - my $crit = sprintf( "%.3f", $cap * 0.1 ); my $datestr = scalar( localtime($lastread) ); print <{name} upload.label Uploaded upload.type GAUGE @@ -199,11 +271,17 @@ upload.graph no upload.colour 00CC00EE download.label Transfer download.type GAUGE -download.info Amount of data downloaded (Limit is $cap GB) download.draw AREA download.extinfo Last Read was $datestr download.negative upload download.colour 00CC00EE +EOF + if ( defined $data->{avail} and $data->{avail} != 0 ) { + my $cap = sprintf( "%.3f", $data->{avail} ); + my $warn = sprintf( "%.3f", $cap * 0.25 ); + my $crit = sprintf( "%.3f", $cap * 0.1 ); + print <{uploadAmount} . "\n"; print "download.value " . $data->{used} . "\n"; -print "allowance.value " . $data->{avail} . "\n"; -my $remain = $data->{avail} - $data->{estusage}; -$remain = 0 if $remain < 0; -print "remaining.value " . $remain . "\n"; -my $overrate = $data->{avedaily} - $data->{maxdaily}; -$overrate = 0 if $overrate < 0; -print "overrate.value " . $overrate . "\n"; +if ( defined $data->{avail} and $data->{avail} != 0 ) { + print "allowance.value " . $data->{avail} . "\n"; + my $remain = $data->{avail} - $data->{estusage}; + $remain = 0 if $remain < 0; + print "remaining.value " . $remain . "\n"; + my $overrate = $data->{avedaily} - $data->{maxdaily}; + $overrate = 0 if $overrate < 0; + print "overrate.value " . $overrate . "\n"; +} save_data($data); exit 0; From 73c0ad77978f5fa9fe2d3ddfdf0c858f1399b913 Mon Sep 17 00:00:00 2001 From: Paul Saunders Date: Mon, 20 Jul 2015 10:45:54 +0100 Subject: [PATCH 004/718] zenus_: Correct compilation errors when Logger not available --- plugins/network/zenus_ | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/network/zenus_ b/plugins/network/zenus_ index 4d8c8ff2..989de72f 100755 --- a/plugins/network/zenus_ +++ b/plugins/network/zenus_ @@ -105,6 +105,8 @@ if ($CAN_LOG) { ); Log::Log4perl::init( \$loggerconf ); our $logger = Log::Log4perl->get_logger($ACCT); +} else { + our $logger = undef; } my $lastread; From 5c671ba157c582415e5b486a247e49be45685a1a Mon Sep 17 00:00:00 2001 From: Paul Saunders Date: Mon, 20 Jul 2015 11:03:25 +0100 Subject: [PATCH 005/718] zenus_: Let's try fixing that compilation error again --- plugins/network/zenus_ | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/network/zenus_ b/plugins/network/zenus_ index 989de72f..319bf2fd 100755 --- a/plugins/network/zenus_ +++ b/plugins/network/zenus_ @@ -106,7 +106,7 @@ if ($CAN_LOG) { Log::Log4perl::init( \$loggerconf ); our $logger = Log::Log4perl->get_logger($ACCT); } else { - our $logger = undef; + our $logger = ""; } my $lastread; @@ -129,7 +129,7 @@ sub save_data { push @save_vector, $_ . '¬' . $hashref->{$_}; } - $logger->info( + $::logger->info( "Saving Data (LastRead is " . localtime($lastread) . "\n" ) if $CAN_LOG; @@ -156,7 +156,7 @@ sub load_data { # Data is stale print "# Data is " . ( time - $lastread ) . " seconds old\n"; - $logger->info( "Data is " . ( time - $lastread ) . " seconds old\n" ) + $::logger->info( "Data is " . ( time - $lastread ) . " seconds old\n" ) if $CAN_LOG; if ( exists $hashref->{backoff} ) { @@ -164,13 +164,13 @@ sub load_data { # We're in a back-off period print "# Back-off in effect\n"; - $logger->info("Back-off in effect\n") if $CAN_LOG; + $::logger->info("Back-off in effect\n") if $CAN_LOG; exit 0; } else { # We've just come out of a back-off period print "# Back-off ends\n"; - $logger->info("Back-off ends\n") if $CAN_LOG; + $::logger->info("Back-off ends\n") if $CAN_LOG; delete $hashref->{backoff}; } } @@ -186,7 +186,7 @@ sub load_data { # back-off for 24 hours. print "# Starting Back-Off\n"; $hashref->{backoff} = time + ( 24 * 60 * 60 ); - $logger->info( + $::logger->info( "Backing off until " . localtime( $hashref->{backoff} ) . "\n" ) if $CAN_LOG; save_data( $hashref, 0 ); @@ -194,7 +194,7 @@ sub load_data { } print "# REFRESHING DATA\n"; - $logger->info("Refreshing Data\n") if $CAN_LOG; + $::logger->info("Refreshing Data\n") if $CAN_LOG; my $temphash; eval { zenus::login( $USER, $PASS ); @@ -223,7 +223,7 @@ sub load_data { } else { print "# Using existing data\n"; - $logger->info("Using existing data\n") if $CAN_LOG; + $::logger->info("Using existing data\n") if $CAN_LOG; } save_data( $hashref, 1 ) if $force_save; return $hashref; From 9cc4259f6e0418b2393f0104ac59bd626d39ed14 Mon Sep 17 00:00:00 2001 From: Niklas Yann Wettengel Date: Mon, 3 Aug 2015 15:37:42 +0200 Subject: [PATCH 006/718] Added handling of multiple lxc.network.veth.pair-lines --- plugins/lxc/lxc_net | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/lxc/lxc_net b/plugins/lxc/lxc_net index 08759087..319a92cb 100755 --- a/plugins/lxc/lxc_net +++ b/plugins/lxc/lxc_net @@ -66,14 +66,16 @@ for guest in `ls $lxcpath`; do continue; fi if [ -f "$lxcpath/$guest/config" ]; then - device=`grep '^lxc\.network\.veth\.pair[ \t]*=[ \t]*' $lxcpath/$guest/config | \ + devices=`grep '^lxc\.network\.veth\.pair[ \t]*=[ \t]*' $lxcpath/$guest/config | \ awk '{ split($0, a, /=/); gsub(/[ \t]/, "", a[2]); print a[2]; }'` - if [ -n "$device" ]; then - device_re=`echo $device | sed -e 's/\./\\\\./g'` - if [ `grep -c "^ *$device_re:" /proc/net/dev` -eq 1 ]; then - actives="$actives $guest" - eval "dev_$(clean_fieldname $guest)=$device" - fi + if [ -n "$devices" ]; then + for device in $devices; do + device_re=`echo $device | sed -e 's/\./\\\\./g'` + if [ `grep -c "^ *$device_re:" /proc/net/dev` -eq 1 ]; then + actives="$actives $guest" + eval "dev_$(clean_fieldname $guest)=$device" + fi + done fi fi done From b17436fe80cbc6f79fa8e108f640253453949e82 Mon Sep 17 00:00:00 2001 From: Paul Saunders Date: Thu, 6 Aug 2015 12:08:55 +0100 Subject: [PATCH 007/718] Rework to tail_open log files --- plugins/znc/znc_logs.py | 218 +++++++++++++++++++++++++++++++--------- 1 file changed, 173 insertions(+), 45 deletions(-) mode change 100644 => 100755 plugins/znc/znc_logs.py diff --git a/plugins/znc/znc_logs.py b/plugins/znc/znc_logs.py old mode 100644 new mode 100755 index 26eee0cc..9305ee52 --- a/plugins/znc/znc_logs.py +++ b/plugins/znc/znc_logs.py @@ -11,7 +11,9 @@ Shows lines/minute in today's znc-logs [znc_logs] user znc # or any other user/group that can read the znclog-folder group znc -env.logdir /var/lib/znc/moddata/log/ # path to the log-folder with a "/" at the end +env.logdir /var/lib/znc/moddata/log/ # path to the GLOBAL log-folder with a "/" at the end +env.expire 0 # Keep channel names forever - OR - +env.expire 1 # Forget channel names from last run =head1 COPYRIGHT GPL VERSION 3 @@ -19,67 +21,193 @@ GPL VERSION 3 =head1 AUTHOR Thor77 ''' -from sys import argv -from time import strftime -from os import environ, listdir +import json +import os, sys, time +import re +import stat +import traceback -logdir = environ.get('logdir') +logdir = os.environ.get('logdir') +expire = os.environ.get('expire', 0) if not logdir: raise Exception('You have to set the logdir with env.logdir in the plugin-conf!') -date = strftime('%Y%m%d') -last_values_file = environ['MUNIN_PLUGSTATE'] + '/last_values' - +date = time.strftime('%Y%m%d') +longdate = time.strftime('%Y-%m-%d') +last_values_file = os.environ['MUNIN_PLUGSTATE'] + '/znc_logs_last' def get_last(): try: d = {} with open(last_values_file, 'r') as f: - for line in f: - line = line[:-1] - key, value = line.split(':') - d[key] = float(value) + d = json.load(f) return d except FileNotFoundError: return {} +def tail_open(filename, position=0): + # Based on tail_open from perls' Munin::Plugin + filereset = 0 + size = os.stat(filename)[stat.ST_SIZE] + if size is None: + return (undef, undef) + f = open(filename, 'r', encoding='utf-8', errors='replace') + if position > size: + filereset = 1 + else: + f.seek(position, 0) + newpos = f.tell() + if newpos != position: + raise Exception + return (f, filereset) -def data(): - last = get_last() - current = {} - for filename in listdir(logdir): - filename_ = filename.replace('.log', '') - network, channel, file_date = filename_.split('_') - network_channel = '{}_{}'.format(network, channel) - # check if log is from today and it is a channel - if file_date == date and channel.startswith('#'): - # current lines in the file - current_value = sum(1 for i in open(logdir + filename, 'r', encoding='utf-8', errors='replace')) - - if network_channel not in last: - value = 0 - else: - last_value = last[network_channel] - # what munin gets - value = (current_value - last_value) / 5 # subtrate last from current and divide through 5 to get new lines / minute - if value < 0: - value = 0 - # save it to the states-file - current[network_channel] = current_value - - # print things to munin - network_channel = network_channel.replace('.', '').replace('#', '') - print('{network_channel}.label {channel}@{network}'.format(network_channel=network_channel, channel=channel, network=network)) - print('{network_channel}.value {value}'.format(network_channel=network_channel, value=value)) - with open(last_values_file, 'w') as f: - for k in current: - f.write('{}:{}\n'.format(k, current[k])) +def tail_close(fh): + position = fh.tell() + fh.close() + return position -if len(argv) > 1 and argv[1] == 'config': +last = get_last() +if "users" in last: + user_list = last["users"] +else: + user_list = {} +if "channels" in last: + channel_list = last["channels"] +else: + channel_list = {} +if "log_pos" in last: + log_pos = last["log_pos"] +else: + log_pos = {} + +channel_stats = {} +user_stats = {} + +def read_data(savestate=True): + # Version 1.6 will change to directory-based filing, so walk recursively + for (dirpath, dirnames, filenames) in os.walk(logdir): + for filename in filenames: + filename_ = filename.replace('.log', '') + + user, network, channel, file_date = (None, None, None, None) + + try: + if len(dirpath) > len(logdir): + # We're below the log path, so this is a 1.6-style log + reldir = dirpath.replace(logdir + "/", '', 1) + try: + network, channel = reldir.split(os.sep) + except ValueError as e: + user, network, channel = reldir.split(os.sep) + file_date = filename_ + else: + try: + network, channel, file_date = filename_.split('_') + except ValueError as e: + user, network, channel, file_date = filename_.split('_') + except ValueError as e: + continue + network_channel = '{}@{}'.format(channel, network) + if network.lower() not in channel_list: + channel_list[network.lower()] = {} + if channel.startswith('#'): + channel_list[network.lower()][channel.lower()] = network_channel + user_list[user.lower()] = user + # check if log is from today + if (file_date == date or file_date == longdate): + # current lines in the file + (fh, r) = tail_open(os.path.join(dirpath,filename), log_pos.get(os.path.join(dirpath, filename), 0)) + current_value = 0 + while True: + where = fh.tell() + line = fh.readline() + if line.endswith('\n'): + current_value += 1 + else: + # Incomplete last line + fh.seek(where, 0) + log_pos[os.path.join(dirpath, filename)] = tail_close(fh) + break + + if network_channel.lower() in channel_stats and channel.startswith('#'): + channel_stats[network_channel.lower()] += current_value + else: + channel_stats[network_channel.lower()] = current_value + + if user is not None and user.lower() in user_stats: + user_stats[user.lower()] += current_value + else: + user_stats[user.lower()] = current_value + if savestate: + savedata = {} + if int(expire) == 0: + savedata["users"] = user_list + savedata["channels"] = channel_list + savedata["log_pos"] = log_pos + with open(last_values_file, 'w') as f: + json.dump(savedata,f) + + +def emit_config(): print('graph_title Lines in the ZNC-log') print('graph_category znc') - print('graph_vlabel lines/minute') + print('graph_vlabel lines / ${graph_period}') print('graph_scale no') -data() + print('graph_args --base 1000 --lower-limit 0') + print('graph_period minute') + graph_order = [] + + if 'MUNIN_CAP_DIRTYCONFIG' in os.environ and os.environ['MUNIN_CAP_DIRTYCONFIG'] == 1: + read_data(1) + else: + read_data(0) + + for network in channel_list.keys(): + for channel in channel_list[network].keys(): + + # print things to munin + network_channel = "{}_{}".format(network,channel).replace('.', '').replace('#', '').replace('@','_') + print('{network_channel}.label {label}'.format(network_channel=network_channel, label=channel_list[network][channel])) + print('{network_channel}.type ABSOLUTE'.format(network_channel=network_channel)) + print('{network_channel}.min 0'.format(network_channel=network_channel)) + print('{network_channel}.draw AREASTACK'.format(network_channel=network_channel)) + + graph_order.append(network_channel) + for user in user_list.keys(): + fuser = re.sub(r'^[^A-Za-z_]', '_', user) + fuser = re.sub(r'[^A-Za-z0-9_]', '_', fuser) + print('{fuser}.label User {user}'.format(fuser=fuser, user=user)) + print('{fuser}.type ABSOLUTE'.format(fuser=fuser)) + print('{fuser}.min 0'.format(fuser=fuser)) + print('{fuser}.draw LINE1'.format(fuser=fuser)) + + print('graph_order {}'.format(" ".join(sorted(graph_order, key=str.lower)))) + +def emit_values(): + read_data(1) + for network in channel_list.keys(): + for channel in channel_list[network].keys(): + + # print things to munin + key = channel_list[network][channel] + network_channel = "{}_{}".format(network,channel).replace('.', '').replace('#', '').replace('@','_') + if key.lower() in channel_stats: + print('{network_channel}.value {value}'.format(network_channel=network_channel, value=channel_stats[key.lower()])) + else: + print('{network_channel}.value U'.format(network_channel=network_channel)) + for user in user_list.keys(): + fuser = re.sub(r'^[^A-Za-z_]', '_', user) + fuser = re.sub(r'[^A-Za-z0-9_]', '_', fuser) + if user.lower() in user_stats: + print('{fuser}.value {value}'.format(fuser=fuser, value=user_stats[user.lower()])) + else: + print('{fuser}.value U'.format(fuser=fuser)) + + +if len(sys.argv) > 1 and sys.argv[1] == 'config': + emit_config() + sys.exit(0) + +emit_values() From 5d778955555e236bb7a6eba00609461b7f12a73b Mon Sep 17 00:00:00 2001 From: Tomas Zvala Date: Tue, 25 Aug 2015 13:46:34 +0200 Subject: [PATCH 008/718] Readd Anonymoust Hugepages (aka. Transparent Hugepages) to system/hugepage plugin. --- plugins/system/hugepages | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/system/hugepages b/plugins/system/hugepages index 51f6a6af..78eca8f5 100755 --- a/plugins/system/hugepages +++ b/plugins/system/hugepages @@ -42,7 +42,7 @@ BEGIN { print "graph_title HugePages usage" print "graph_category system" print "graph_info This graph shows the usage of the kernel Huge Pages." - print "graph_order Total Rsvd Free Surp" + print "graph_order Total Rsvd Free Surp Anon" print "Total.label total" print "Total.draw AREA" print "Total.info Size of pool of hugepages ('nr_hugepages')" @@ -55,6 +55,9 @@ BEGIN { print "Surp.label surplus" print "Surp.draw STACK" print "Surp.info Number of hugepages > nr_hugepages, as decided by nr_overcommit_hugepages or when the amount of Huge Pages is reduced while they are in use." + print "Anon.label Transparent" + print "Anon.draw STACK" + print "Anon.info Huge Pages that are in use by the transparent Huge Page allocator khugepaged." CONF=1 } if (ARGC > 1 && ARGV[1] == "autoconf") { @@ -69,6 +72,7 @@ BEGIN { CONF == 1 { if (/Hugepagesize/) { + print "Anon.cdef Anon,",$2,",/" print "graph_vlabel Pages (",$2,"KB/page)" } } From 19ca132a4688ff921766a8ff9e18c746c76faa36 Mon Sep 17 00:00:00 2001 From: rkarlsba Date: Fri, 28 Aug 2015 12:07:04 +0200 Subject: [PATCH 009/718] ALL keyword Add ALL keyword to count cpu use of all users. May be a dirty hack, but not sure how to do this otherwise... --- plugins/system/cpubyuser | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index 424bc2cf..c2522233 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -7,6 +7,8 @@ # [cpubyuser] # env.USERS root yann # +# If env.USERS is set to ALL, count all logged in users. +# # root and yann being a list of the users to monitor. # You need to also make sure that awk is installed # @@ -70,6 +72,10 @@ if [ "$1" = "config" ]; then exit fi +if [ "$USERS" = "ALL" ]; then + USERS=$( w | awk '{ print $1 }' ) +fi + top -b -n 1 | tail -n +8 | \ awk -v USERS="$USERS" ' { if ($2 != "USER") CPU_USER[$2]+=$9 } From c9e77c391a73fbbd866c8365f66e4c3ebaca75c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Korn?= Date: Sun, 20 Sep 2015 20:15:53 +0200 Subject: [PATCH 010/718] * Switch to zsh (used to be bash) * Fix to remove dots from vserver names (replace them with underscores; dots confuse munin) * Drop support for old (pre 2.6.19) kernels * Replace cat | grep | cut pipelines with a single sed each * Add env.stripdomain (a domain name to strip from the end of vserver names when generating labels; be sure to include the leading dot) --- .../virtualization/vserver/vserver_jiffies | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/plugins/virtualization/vserver/vserver_jiffies b/plugins/virtualization/vserver/vserver_jiffies index 2e139a63..c405d703 100755 --- a/plugins/virtualization/vserver/vserver_jiffies +++ b/plugins/virtualization/vserver/vserver_jiffies @@ -1,7 +1,19 @@ -#!/bin/bash +#!/bin/zsh # # Created by Jan RÄ™korajski based on vserver_cpu_ plugin. # +# Obtained from https://github.com/munin-monitoring/contrib.git +# +# Changes by Andras Korn, 2015: +# +# * Convert to zsh +# * Fix to remove dots from vserver names (replace them with _) +# * Drop support for old (pre 2.6.19) kernels +# * Replace cat | grep | cut pipelines with a single sed +# * Add env.stripdomain (a domain name to strip from the +# end of vserver names when generating labels; be sure +# to include the leading dot) +# # Graph Vserver cumulative cpu usage stats # # Configuration variables @@ -11,47 +23,45 @@ # # see vserver_resources for example uses of configuration files -VSERVERS="$vservers" +VSERVERS=(${=vservers}) +STRIPDOMAIN="$stripdomain" -INFO=(`sed 's/.*:\t//' /proc/virtual/info 2>/dev/null || echo ''`) +INFO=($(sed 's/.*:\t//' /proc/virtual/info 2>/dev/null || echo '')) KCIN="$[ 16#${INFO[2]} ]"; -# If this is 1, then VCI_SPACES is present in the kernel (new in 2.6.19) -if [ $[ (KCIN >> 10) & 1 ] -eq 1 ] -then - NAMELOC="nsproxy" -else - NAMELOC="cvirt" -fi +NAMELOC="nsproxy" -if [ -z "$VSERVERS" ] ; then - XIDS=`find /proc/virtual/* -type d -exec basename {} \;` +if [[ -z "$VSERVERS" ]] ; then + cd /proc/virtual + XIDS=($(echo *(/))) else # it's really more performant to specify vservers by ids or by linking but not in the configuration-file by name XIDS="" - for i in $VSERVERS ; do - if [ -d /proc/virtual/$i ] ; then - XIDS="${XIDS}${i} " + for i in $VSERVERS[@] ; do + if [[ -d /proc/virtual/$i ]] ; then + XIDS=($XIDS[@] $i) else - for j in `find /proc/virtual/* -type d -exec basename {} \;` ; do - if [ "$i" = "`cat /proc/virtual/$j/$NAMELOC |grep NodeName |cut -f2`" ] ; then - XIDS="${XIDS}${j} " + for j in /proc/virtual/*(/) -type d; do + if [[ "$i" = $(sed -n '/NodeName/s/^NodeName:[[:space:]]*//p' $j/$NAMELOC) ]]; then + XIDS=($XIDS[@] ${j:t}) fi done fi done fi -if [ "$1" = "config" ]; then +if [[ "$1" = "config" ]]; then echo 'graph_category Virtualization' echo 'graph_args --base 1000' echo 'graph_title Vserver cpu usage' echo 'graph_vlabel jiffies used per ${graph_period}' echo 'graph_info Shows jiffies used on each vserver.' - for i in $XIDS ; do - LABEL=`grep NodeName /proc/virtual/$i/$NAMELOC | cut -f2` - NAME=`echo $LABEL | tr '-' '_'` + for i in $XIDS[@]; do + LABEL=$(sed -n '/NodeName/s/^NodeName:[[:space:]]*//p' /proc/virtual/$i/$NAMELOC) + LABEL=${LABEL%$STRIPDOMAIN} + NAME=${LABEL//./_} + NAME=${NAME//-/_} echo "${NAME}_hold.label on hold for cpu on $LABEL" echo "${NAME}_hold.info on hold for cpu on $LABEL." echo "${NAME}_hold.type COUNTER" @@ -65,8 +75,11 @@ if [ "$1" = "config" ]; then exit 0 fi -for i in $XIDS ; do - NAME=`grep NodeName /proc/virtual/$i/$NAMELOC | cut -f2 | tr '-' '_'` +for i in $XIDS[@]; do + LABEL=$(sed -n '/NodeName/s/^NodeName:[[:space:]]*//p' /proc/virtual/$i/$NAMELOC) + LABEL=${LABEL%$STRIPDOMAIN} + NAME=${LABEL//./_} + NAME=${NAME//-/_} awk -v name=$NAME -v u=0 -v s=0 -v h=0 ' /^cpu [0-9]+:/ { u+=$3; s+=$4; h+=$5} END { From a6ea4c428c7379fc054d866fe8b5616f4ccf0047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Korn?= Date: Sat, 26 Sep 2015 17:25:54 +0200 Subject: [PATCH 011/718] Add zsh syntax check to test.t --- t/test.t | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/t/test.t b/t/test.t index a12151fb..a11c7137 100644 --- a/t/test.t +++ b/t/test.t @@ -79,6 +79,14 @@ sub process_file { } ); } + elsif ( $interpreter =~ m{/bin/zsh} ) { + run_check( + { command => [ 'zsh', '-n', $file ], + description => 'zsh syntax check', + filename => $filename + } + ); + } elsif ( $interpreter =~ m{perl} ) { my $command; if ( $arguments =~ m{-.*T}mx ) { From 68d23cce869005cf4eed31a291b4c912986d457e Mon Sep 17 00:00:00 2001 From: Takuya Matsuyama Date: Fri, 9 Oct 2015 22:10:39 +0900 Subject: [PATCH 012/718] Fix bug where can't read lock ratio --- plugins/mongodb/mongo_lock | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/mongodb/mongo_lock b/plugins/mongodb/mongo_lock index 4b94aeca..7b059df7 100755 --- a/plugins/mongodb/mongo_lock +++ b/plugins/mongodb/mongo_lock @@ -20,7 +20,10 @@ name = "locked" def doData(): status = getServerStatus() if status["version"] >= "2.2.0": - ratio = float(status["globalLock"]["lockTime"]) / status["globalLock"]["totalTime"] + if status["globalLock"]["lockTime"]["$numberLong"]: + ratio = float(status["globalLock"]["lockTime"]["$numberLong"]) / float(status["globalLock"]["totalTime"]["$numberLong"]) + else: + ratio = float(status["globalLock"]["lockTime"]) / status["globalLock"]["totalTime"] else: ratio = status["globalLock"]["ratio"] print name + ".value " + str( 100 * ratio ) From 1724ae250437405eb92f67ff8642daf91acd05bb Mon Sep 17 00:00:00 2001 From: Babak Farrokhi Date: Wed, 28 Oct 2015 14:42:10 +0330 Subject: [PATCH 013/718] - Fix matching table names that include "-" charachter - Fix drawing graph for inout graphs --- plugins/network/pf_tables_ | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/network/pf_tables_ b/plugins/network/pf_tables_ index 3e98903f..0f3975ae 100644 --- a/plugins/network/pf_tables_ +++ b/plugins/network/pf_tables_ @@ -113,19 +113,18 @@ if ( defined($ARGV[0])) { print "in.label in\n"; print "in.type DERIVE\n"; - print "in.draw AREASTACK\n"; + print "in.draw AREA\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.label bps\n"; print "out.type DERIVE\n"; print "out.negative in\n"; - print "out.draw AREASTACK\n"; - print "out.colour 0000C0\n"; + print "out.draw AREA\n"; + print "out.colour COLOUR18\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); @@ -191,9 +190,10 @@ sub tables { foreach (split(/\n/, $output)) { - if (m|^[cpairhC\-]{7}\s+(\w+)$|) { + if (m|^[cpairhC\-]{7}\s+(\S+)$|) { $name = $1; - $tables{$name}->{"name"} = $1; + $name =~ s/\-/_/; + $tables{$name}->{"name"} = $name; next; } From 4de0ceb073d0f2e1a49684e7c155a164b5b1b5b4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 29 Oct 2015 03:45:38 +0100 Subject: [PATCH 014/718] new network plugin added: wireless_channel_occupation This plugin tracks the occupation of the shared medium (wifi channel). It uses the "iw" tool for extracting the time slices spent by the wireless driver receiving, transmitting or ignoring (noise) on the current channel. This ratio is a good indication for the remaining capacity of the shared communication medium. --- plugins/network/wireless_channel_occupation_ | 105 +++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100755 plugins/network/wireless_channel_occupation_ diff --git a/plugins/network/wireless_channel_occupation_ b/plugins/network/wireless_channel_occupation_ new file mode 100755 index 00000000..71c54c3e --- /dev/null +++ b/plugins/network/wireless_channel_occupation_ @@ -0,0 +1,105 @@ +#!/bin/sh +# +# Monitor the wifi channel occupation (taken from "iw dev wlan0 survey dump"). +# +# Symlink this plugin with the name of the wifi interface added (e.g. "wlan0"). +# +# +# 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 + +set -eu + + +SCRIPT_PREFIX="wireless_channel_occupation_" + + +clean_fieldname() { + echo "$1" | sed 's/^\([^A-Za-z_]\)/_\1/; s/[^A-Za-z0-9_]/_/g' +} + + +get_wifi_devices() { + iw dev | grep Interface | awk '{print $2}' +} + + +get_wifi_device_from_suffix() { + local suffix + local called + called=$(basename "$0") + suffix="${called#$SCRIPT_PREFIX}" + # empty result if the prefix did not match (and was not removed) + [ "$suffix" = "$0" ] && echo "" || echo "$suffix" +} + + +if [ "${1:-}" = "autoconf" ]; then + if which iw 2>/dev/null; then + if [ -n "$(get_wifi_devices)" ]; then + echo "yes" + else + echo "no (missing wifi devices)" + fi + else + echo "no (missing 'iw' dependency)" + fi +elif [ "${1:-}" = "suggest" ]; then + get_wifi_devices +elif [ "${1:-}" = "config" ]; then + device=$(get_wifi_device_from_suffix) + [ -z "$device" ] && echo >&2 "Invalid wifi device name given" && exit 1 + echo "graph_title Channel Occupation of $device" + echo "graph_vlabel %" + echo "graph_category wireless" + echo "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100" + dev_field=$(clean_fieldname "$device") + + # active: listening time on this channel (usually: 5 minutes = 300000ms) + echo "${dev_field}_active.label Transmit" + echo "${dev_field}_active.type DERIVE" + echo "${dev_field}_active.graph no" + + # busy = receive + transmit + unknown + echo "${dev_field}_busy.label unknown" + echo "${dev_field}_busy.type DERIVE" + echo "${dev_field}_busy.draw AREA" + echo "${dev_field}_busy.cdef 100,1,${dev_field}_active,${dev_field}_busy,${dev_field}_receive,${dev_field}_transmit,+,-,/,/,*" + + # receive: this radio receives traffic for itself + echo "${dev_field}_receive.label Receive" + echo "${dev_field}_receive.type DERIVE" + echo "${dev_field}_receive.draw STACK" + echo "${dev_field}_receive.cdef 100,${dev_field}_receive,${dev_field}_active,/,*" + + # transmit: this radio transmits traffic + echo "${dev_field}_transmit.label Transmit" + echo "${dev_field}_transmit.type DERIVE" + echo "${dev_field}_transmit.draw STACK" + echo "${dev_field}_transmit.cdef 100,${dev_field}_transmit,${dev_field}_active,/,*" +else + device=$(get_wifi_device_from_suffix) + [ -z "$device" ] && echo >&2 "Invalid wifi device name given" && exit 1 + iw dev "$device" survey dump \ + | grep -F -A 5 "[in use]" \ + | grep -E "channel (busy|receive|transmit|active) time:" \ + | awk '{print "'${device}_'"$2"'.value'",$4}' +fi + +exit 0 From 1c281c61d93bf6a899fad3c8225737c4765006dd Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 29 Oct 2015 03:51:25 +0100 Subject: [PATCH 015/718] new network plugin added: olsrd Collect basic information about the neighbours of an OLSR node: * link quality * neighbour link quality * number of nodes reachable behind each neighbour * ping times of direct neighbours OLSR is a routing protocol (layer 3). --- plugins/network/olsrd | 340 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100755 plugins/network/olsrd diff --git a/plugins/network/olsrd b/plugins/network/olsrd new file mode 100755 index 00000000..23bdf02a --- /dev/null +++ b/plugins/network/olsrd @@ -0,0 +1,340 @@ +#!/bin/sh +# weird shebang? See below: "interpreter selection" +# +# Collect basic information about the neighbours of an OLSR node: +# * link quality +# * neighbour link quality +# * number of nodes reachable behind each neighbour +# * ping times of direct neighbours +# +# This plugin works with the following python interpreters: +# * Python 2 +# * Python 3 +# * micropython +# +# Environment variables: +# * OLSRD_HOST: name or IP of the host running the txtinfo plugin (default: localhost) +# * OLSRD_TXTINFO_PORT: the port that the txtinfo plugin is listening to (default: 2006) +# * OLSRD_BIN_PATH: name of the olsrd binary (only used for 'autoconf', defaults to /usr/sbin/olsrd) +# * MICROPYTHON_HEAP: adjust this parameter for micropython if your olsr network contains +# more than a few thousand nodes (default: 512k) +# +# +# 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 +#%# family=auto + +"""true" +# ****************** Interpreter Selection *************** +# This unbelievable dirty hack allows to find a suitable python interpreter. +# This is specifically useful for OpenWRT where typically only micropython is available. +# +# Additionally we need to run micropython with additional startup options. +# This is necessary due to our demand for more than 128k heap (this default is sufficient for only 400 olsr nodes). +# +# This "execution hack" works as follows: +# * the script is executed by busybox ash or another shell +# * the above line (three quotes before and one quote after 'true') evaluates differently for shell and python: +# * shell: run "true" (i.e. nothing happens) +# * python: ignore everything up to the next three consecutive quotes +# Thus we may place shell code here that will take care for selecting an interpreter. + +# prefer micropython if it is available - otherwise fall back to any python (2 or 3) +if which micropython >/dev/null; then + /usr/bin/micropython -X "heapsize=${MICROPYTHON_HEAP:-512k}" "$0" "$@" +else + python "$0" "$@" +fi +exit $? +""" + + +plugin_version = "0.3" + + +import os +import socket +import sys + + +LQ_GRAPH_CONFIG = """ +graph_title {title} +graph_vlabel Link Quality (-) / Neighbour Link Quality (+) +graph_category network +graph_info OLSR estimates the quality of a connection by the ratio of successfully received (link quality) and transmitted (neighbour link quality) hello packets. +""" + +LQ_VALUES_CONFIG = """ +nlq{suffix}.label none +nlq{suffix}.type GAUGE +nlq{suffix}.graph no +nlq{suffix}.draw {draw_type} +nlq{suffix}.min 0 +lq{suffix}.label {label} +lq{suffix}.type GAUGE +lq{suffix}.draw {draw_type} +lq{suffix}.negative nlq{suffix} +lq{suffix}.min 0 +""" + +NEIGHBOUR_COUNT_CONFIG = """ +graph_title Reachable nodes via neighbours +graph_vlabel Number of Nodes +graph_category network +graph_info Count the number of locally known routes passing through each direct neighbour. This number is a good approximation of the number of mesh nodes reachable via this specific neighbour. MIDs (alternative addresses of an OLSR node) and HNAs (host network announcements) are ignored. +""" + +NEIGHBOUR_COUNT_VALUE = """ +neighbour_{host_fieldname}.label {host} +neighbour_{host_fieldname}.type GAUGE +neighbour_{host_fieldname}.draw {draw_type} +neighbour_{host_fieldname}.min 0 +""" + +NEIGHBOUR_PING_CONFIG = """ +graph_title {title} +graph_vlabel roundtrip time (ms) +graph_category network +graph_info This graph shows ping RTT statistics. +graph_args --base 1000 --lower-limit 0 +graph_scale no +""" + +NEIGHBOUR_PING_VALUE = """neighbour_{host_fieldname}.label {host}""" + +# micropython (as of 2015) does not contain "os.linesep" +LINESEP = getattr(os, "linesep", "\n") + + +get_clean_fieldname = lambda name: "".join([(("a" <= char.lower() <= "z") or ((index == 0) or ("0" <= char <= "9"))) and char or "_" for index, char in enumerate(name)]) + + +def query_olsrd_txtservice(section=""): + host = os.getenv("OLSRD_HOST", "localhost") + port = os.getenv("OLSRD_TXTINFO_PORT", "2006") + conn = socket.create_connection((host, port), 1.0) + try: + # Python3 + request = bytes("/%s" % section, "ascii") + except TypeError: + # Python2 + request = bytes("/%s" % section) + conn.sendall(request) + fconn = conn.makefile() + # "enumerate" is not suitable since it reads all lines at once (not a generator in micropython) + index = 0 + for line in fconn.readlines(): + # skip the first five lines - they are headers + if index < 5: + index += 1 + continue + line = line.strip() + if line: + yield line + fconn.close() + conn.close() + + +def get_address_device_mapping(): + mapping = {} + for line in query_olsrd_txtservice("mid"): + # example line content: + # 192.168.2.171 192.168.22.171;192.168.12.171 + device_id, mids = line.split() + for mid in mids.split(";"): + mapping[mid] = device_id + return mapping + + +def count_routes_by_neighbour(address_mapping, ignore_list): + node_count = {} + for line in query_olsrd_txtservice("routes"): + # example line content: + # 192.168.1.79/32 192.168.12.38 4 4.008 wlan0 + tokens = line.split() + target = tokens[0] + via = tokens[1] + # we care only about single-host routes + if target.endswith("/32"): + if target[:-3] in address_mapping: + # we ignore MIDs - we want only real nodes + continue + if target in ignore_list: + continue + # replace the neighbour's IP with its main IP (if it is an MID) + via = address_mapping.get(via, via) + # increase the counter + node_count[via] = node_count.get(via, 0) + 1 + return node_count + + +def get_olsr_links(): + mid_mapping = get_address_device_mapping() + hna_list = [line.split()[0] for line in query_olsrd_txtservice("hna")] + route_count = count_routes_by_neighbour(mid_mapping, hna_list) + result = [] + for line in query_olsrd_txtservice("links"): + tokens = line.split() + link = {} + link["local"] = tokens.pop(0) + remote = tokens.pop(0) + # replace the neighbour's IP with its main IP (if it is an MID) + link["remote"] = mid_mapping.get(remote, remote) + for key in ("hysterese", "lq", "nlq", "cost"): + link[key] = float(tokens.pop(0)) + # add the route count + link["route_count"] = route_count.get(link["remote"], 0) + result.append(link) + result.sort(key=lambda link: link["remote"]) + return result + + +def _read_file(filename): + try: + return open(filename, "r").read().split(LINESEP) + except OSError: + return [] + + +def get_ping_times(hosts): + tempfile = "/tmp/munin-olsrd-{pid}.tmp".format(pid=os.getpid()) + command = 'for host in {hosts}; do echo -n "$host "; ping -c 1 -w 1 "$host" | grep /avg/ || true; done >{tempfile}'\ + .format(hosts=" ".join(hosts), tempfile=tempfile) + # micropython supports only "os.system" (as of 2015) - thus we need to stick with it for openwrt + returncode = os.system(command) + if returncode != 0: + return {} + lines = _read_file(tempfile) + os.unlink(tempfile) + # example output for one host: + # 192.168.2.41 round-trip min/avg/max = 4.226/4.226/4.226 ms + result = {} + for line in lines: + tokens = line.split(None) + if len(tokens) > 1: + host = tokens[0] + avg_ping = tokens[-2].split("/")[1] + result[host] = float(avg_ping) + return result + + +if __name__ == "__main__": + # parse arguments + if len(sys.argv) > 1: + if sys.argv[1]=="config": + links = list(get_olsr_links()) + + # link quality with regard to neighbours + print("multigraph olsr_link_quality") + print(LQ_GRAPH_CONFIG.format(title="OLSR Link Quality")) + is_first = True + for link in links: + print(LQ_VALUES_CONFIG.format(label=link["remote"], + suffix="_{host}".format(host=get_clean_fieldname(link["remote"])), + draw_type=("AREA" if is_first else "STACK"))) + is_first = False + is_first = True + for link in links: + print("multigraph olsr_link_quality.host_{remote}".format(remote=get_clean_fieldname(link["remote"]))) + print(LQ_GRAPH_CONFIG.format(title="Link Quality towards {host}".format(host=link["remote"]))) + print(LQ_VALUES_CONFIG.format(label="Link Quality", suffix="", draw_type="AREA")) + is_first = False + + # link count ("number of nodes behind each neighbour") + print("multigraph olsr_neighbour_link_count") + print(NEIGHBOUR_COUNT_CONFIG) + is_first = True + for link in links: + print(NEIGHBOUR_COUNT_VALUE + .format(host=link["remote"], + host_fieldname=get_clean_fieldname(link["remote"]), + draw_type=("AREA" if is_first else "STACK"))) + is_first = False + + # neighbour ping + print("multigraph olsr_neighbour_ping") + print(NEIGHBOUR_PING_CONFIG.format(title="Ping time of neighbours")) + for link in links: + print(NEIGHBOUR_PING_VALUE + .format(host=link["remote"], + host_fieldname=get_clean_fieldname(link["remote"]))) + # neighbour pings - single subgraphs + for link in links: + remote = get_clean_fieldname(link["remote"]) + print("multigraph olsr_neighbour_ping.host_{remote}".format(remote=remote)) + print(NEIGHBOUR_PING_CONFIG.format(title="Ping time of {remote}".format(remote=remote))) + print(NEIGHBOUR_PING_VALUE.format(host=link["remote"], host_fieldname=remote)) + + sys.exit(0) + elif sys.argv[1] == "autoconf": + if os.path.exists(os.getenv('OLSRD_BIN_PATH', '/usr/sbin/olsrd')): + print('yes') + else: + print('no') + sys.exit(0) + elif sys.argv[1] == "version": + print('olsrd Munin plugin, version %s' % plugin_version) + sys.exit(0) + elif sys.argv[1] == "": + # ignore + pass + else: + # unknown argument + sys.stderr.write("Unknown argument{eol}".format(eol=LINESEP)) + sys.exit(1) + + # output values + links = list(get_olsr_links()) + + # overview graph for the link quality (ETX) of all neighbours + print("multigraph olsr_link_quality") + for link in links: + print("lq_{remote}.value {lq:f}".format(lq=link["lq"], + remote=get_clean_fieldname(link["remote"]))) + print("nlq_{remote}.value {nlq:f}".format(nlq=link["nlq"], + remote=get_clean_fieldname(link["remote"]))) + # detailed ETX graph for each single neighbour link + for link in links: + print("multigraph olsr_link_quality.host_{remote}" + .format(remote=get_clean_fieldname(link["remote"]))) + print("lq.value {lq:f}".format(lq=link["lq"])) + print("nlq.value {nlq:f}".format(nlq=link["nlq"])) + + # count the links/nodes behind each neighbour node + print("multigraph olsr_neighbour_link_count") + for link in links: + print("neighbour_{host_fieldname}.value {value}" + .format(value=link["route_count"], + host_fieldname=get_clean_fieldname(link["remote"]))) + + # overview of ping roundtrip times + print("multigraph olsr_neighbour_ping") + ping_times = get_ping_times([link["remote"] for link in links]) + for link in links: + ping_time = ping_times.get(link["remote"], None) + if ping_time is not None: + print("neighbour_{remote}.value {value:.4f}" + .format(value=ping_time, + remote=get_clean_fieldname(link["remote"]))) + # single detailed graphs for the ping time of each link + for link in links: + ping_time = ping_times.get(link["remote"], None) + if ping_time is not None: + remote = get_clean_fieldname(link["remote"]) + print("multigraph olsr_neighbour_ping.host_{remote}".format(remote=remote)) + print("neighbour_{remote}.value {value:.4f}".format(remote=remote, value=ping_time)) From ec55d6cce2f71a0b949f414930512bf4dc865f88 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 29 Oct 2015 03:57:00 +0100 Subject: [PATCH 016/718] new network plugin added: ath9k Collect information related to ath9k wireless events and states. * rate control statistics ("rc_stats") * events (dropped, transmitted, beacon loss, ...) * traffic (packets, bytes) All data is collected for each separate station (in case of multiple connected peers). Combined graphs are provided as a summary. --- plugins/network/ath9k_ | 453 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100755 plugins/network/ath9k_ diff --git a/plugins/network/ath9k_ b/plugins/network/ath9k_ new file mode 100755 index 00000000..81229123 --- /dev/null +++ b/plugins/network/ath9k_ @@ -0,0 +1,453 @@ +#!/bin/sh +# weird shebang? See below: "interpreter selection" +# +# Collect information related to ath9k wireless events and states. +# * rate control statistics ("rc_stats") +# * events (dropped, transmitted, beacon loss, ...) +# * traffic (packets, bytes) +# +# All data is collected for each separate station (in case of multiple +# connected peers). Combined graphs are provided as a summary. +# +# +# This plugin works with the following python interpreters: +# * Python 2 +# * Python 3 +# * micropython +# +# +# 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 + +"""true" +# ****************** Interpreter Selection *************** +# This unbelievable dirty hack allows to find a suitable python interpreter. +# This is specifically useful for OpenWRT where typically only micropython is available. +# +# This "execution hack" works as follows: +# * the script is executed by busybox ash or another shell +# * the above line (three quotes before and one quote after 'true') evaluates differently for shell and python: +# * shell: run "true" (i.e. nothing happens) +# * python: ignore everything up to the next three consecutive quotes +# Thus we may place shell code here that will take care for selecting an interpreter. + +# prefer micropython if it is available - otherwise fall back to any python (2 or 3) +if which micropython >/dev/null; then + /usr/bin/micropython "$0" "$@" +else + python "$0" "$@" +fi +exit $? +""" + + +""" +The following graphs are generated for each physical ath9k interface: + phy0_wifi0_traffic + phy0_wifi0_traffic.station0 + ... + pyh0_wifi0_events + phy0_wifi0_events.station0 + ... + pyh0_wifi0_rc_stats + phy0_wifi0_rc_stats.station0 + ... +""" + + +plugin_version = "0.2" + +STATION_TRAFFIC_COUNTERS = ("rx_bytes", "tx_bytes", "rx_packets", "tx_packets") +STATION_EVENT_COUNTERS = ("tx_retry_count", "tx_retry_failed", "tx_filtered", "tx_fragments", + "rx_dropped", "rx_fragments", "rx_duplicates", "beacon_loss_count") +# 16 colors (see http://munin-monitoring.org/wiki/fieldname.colour) for visualizing +# rate control selection (see rc_stats) +QUALITY_GRAPH_COLORS_16 = ("FF1F00", "FF4500", "FF7000", "FF9700", + "FFBC00", "FAE600", "D1FF00", "7BFF00", + "1CFF00", "06E41B", "00C43B", "009D60", + "007986", "0058A8", "0033CC", "0018DE") +SYS_BASE_DIR = "/sys/kernel/debug/ieee80211" +GRAPH_BASE_NAME = "ath9k_stats" +PLUGIN_SCOPES = ("traffic", "events", "rcstats") + + +import os +import os.path +import subprocess +import sys + + +class Station: + + config_map = {"events": lambda station, **kwargs: station._get_events_config(**kwargs), + "traffic": lambda station, **kwargs: station._get_traffic_config(**kwargs), + "rcstats": lambda station, **kwargs: station._get_rc_stats_config(**kwargs)} + values_map = {"events": lambda station: station._events_stats, + "traffic": lambda station: station._traffic_stats, + "rcstats": lambda station: station._get_rc_stats_success()} + + def __init__(self, label, key, path): + self._path = path + self.label = label + self.key = key + self._events_stats = self._parse_file_based_stats(STATION_EVENT_COUNTERS) + self._traffic_stats = self._parse_file_based_stats(STATION_TRAFFIC_COUNTERS) + self._rc_stats = self._parse_rc_stats() + + def _parse_rc_stats(self): + """ example content + + type rate tpt eprob *prob ret *ok(*cum) ok( cum) + HT20/LGI MCS0 5.6 100.0 100.0 3 0( 0) 3( 3) + HT20/LGI MCS1 10.5 100.0 100.0 0 0( 0) 1( 1) + HT20/LGI MCS2 14.9 100.0 100.0 0 0( 0) 1( 1) + HT20/LGI MCS3 18.7 96.5 100.0 5 0( 0) 261( 328) + HT20/LGI MCS4 25.3 95.6 100.0 5 0( 0) 4267( 5460) + HT20/LGI MCS5 30.6 95.8 100.0 5 0( 0) 11735( 17482) + HT20/LGI MCS6 32.9 95.7 100.0 5 0( 0) 24295( 32592) + HT20/LGI DP MCS7 35.0 90.4 95.2 5 0( 0) 63356( 88600) + HT20/LGI MCS8 10.5 100.0 100.0 0 0( 0) 1( 1) + + beware: sometimes the last two pairs of columns are joined without withespace: "90959383(100188029)" + """ + stats = {} + with open(os.path.join(self._path, "rc_stats"), "r") as statsfile: + rate_column = None + skip_retry_column = False + for index, line in enumerate(statsfile.readlines()): + # remove trailing linebreak, replace braces (annoyingly present in the lasf four columns) + line = line.rstrip().replace("(", " ").replace(")", " ") + # ignore the trailing summary lines + if not line: + break + if index == 0: + # we need to remember the start of the "rate" column (in order to skip the flags) + rate_column = line.index("rate") + if rate_column == 0: + # the following weird format was found on a Barrier Breaker host (2014, Linux 3.10.49): + # rate throughput ewma prob this prob this succ/attempt success attempts + # ABCDP 6 5.4 89.9 100.0 0( 0) 171 183 + # Thus we just assume that there are five flag letters and two blanks. + # Let's hope for the best! + rate_column = 6 + # this format does not contain the "retry" column + skip_retry_column = True + # skip the header line + continue + cutoff_line = line[rate_column:] + tokens = cutoff_line.split() + entry = {} + entry["rate"] = tokens.pop(0) + entry["throughput"] = float(tokens.pop(0)) + entry["ewma_probability"] = float(tokens.pop(0)) + entry["this_probability"] = float(tokens.pop(0)) + if skip_retry_column: + entry["retry"] = 0 + else: + entry["retry"] = int(tokens.pop(0)) + entry["this_success"] = int(tokens.pop(0)) + entry["this_attempts"] = int(tokens.pop(0)) + entry["success"] = int(tokens.pop(0)) + entry["attempts"] = int(tokens.pop(0)) + # some "rate" values are given in MBit/s - some are MCS0..15 + try: + entry["rate_label"] = "{rate:d} MBit/s".format(rate=int(entry["rate"])) + except ValueError: + # keep the MCS string + entry["rate_label"] = entry["rate"] + stats[entry["rate"]] = entry + return stats + + def _get_rc_stats_success(self): + rc_values = {self._get_rate_fieldname(rate["rate"]): rate["success"] for rate in self._rc_stats.values()} + rc_values["sum"] = sum(rc_values.values()) + return rc_values + + def _parse_file_based_stats(self, counters): + stats = {} + for counter in counters: + # some events are not handled with older versions (e.g. "beacon_loss_count") + filename = os.path.join(self._path, counter) + if os.path.exists(filename): + content = open(filename, "r").read().strip() + stats[counter] = int(content) + return stats + + def get_values(self, scope, graph_base): + func = self.values_map[scope] + yield "multigraph {base}_{suffix}.{station}".format(base=graph_base, suffix=scope, station=self.key) + for key, value in func(self).items(): + yield "{key}.value {value}".format(key=key, value=value) + yield "" + + @classmethod + def get_summary_values(cls, scope, siblings, graph_base): + func = cls.values_map[scope] + yield "multigraph {base}_{suffix}".format(base=graph_base, suffix=scope) + stats = {} + for station in siblings: + for key, value in func(station).items(): + stats[key] = stats.get(key, 0) + value + for key, value in stats.items(): + yield "{key}.value {value}".format(key=key, value=value) + yield "" + + def get_config(self, scope, graph_base): + func = self.config_map[scope] + yield "multigraph {base}_{suffix}.{station}".format(base=graph_base, suffix=scope, station=self.key) + yield from func(self, label=self.label, siblings=[self]) + + @classmethod + def get_summary_config(cls, scope, siblings, graph_base): + func = cls.config_map[scope] + yield "multigraph {base}_{suffix}".format(base=graph_base, suffix=scope) + for station in siblings: + yield from func(station, siblings=[station]) + + @classmethod + def _get_traffic_config(cls, label=None, siblings=None): + if label: + yield "graph_title ath9k Station Traffic {label}".format(label=label) + else: + yield "graph_title ath9k Station Traffic" + yield "graph_args --base 1024" + yield "graph_vlabel received (-) / transmitted (+)" + yield "graph_category wireless" + # convert bytes/s into kbit/s (x * 8 / 1000 = x / 125) + yield from _get_up_down_pair("kBit/s", "tx_bytes", "rx_bytes", divider=125, use_negative=False) + yield from _get_up_down_pair("Packets/s", "tx_packets", "rx_packets", use_negative=False) + yield "" + + @classmethod + def _get_events_config(cls, label=None, siblings=None): + if label: + yield "graph_title ath9k Station Events {label}".format(label=label) + else: + yield "graph_title ath9k Station Events" + yield "graph_vlabel events per ${graph_period}" + yield "graph_category wireless" + events = set() + for station in siblings: + for event in STATION_EVENT_COUNTERS: + events.add(event) + for event in events: + yield "{event}.label {event}".format(event=event) + yield "{event}.type COUNTER".format(event=event) + yield "" + + @classmethod + def _get_rate_fieldname(cls, rate): + return "rate_{0}".format(rate.lower()).replace(".", "_") + + @classmethod + def _get_rc_stats_config(cls, label=None, siblings=None): + if label: + yield "graph_title ath9k Station Transmit Rates {label} Success".format(label=label) + else: + yield "graph_title ath9k Station Transmit Rates Success" + yield "graph_vlabel transmit rates %" + yield "graph_category wireless" + yield "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100" + all_rates = {} + # collect alle unique rates + for station in siblings: + for rate, details in station._rc_stats.items(): + all_rates[rate] = details + # return all rates + is_first = True + num_extract = lambda text: int("".join([char for char in text if "0" <= char <= "9"])) + get_key = lambda rate_name: cls._get_rate_fieldname(all_rates[rate_name]["rate"]) + # add all rates for percent visualization ("MCS7,MCS6,MCS5,MCS4,MCS3,MCS2,MCS1,MCS0,+,+,+,+,+,+,+") + cdef = None + for sum_rate in all_rates: + if cdef is None: + cdef = get_key(sum_rate) + else: + cdef = "{key},{cdef},+".format(key=get_key(sum_rate), cdef=cdef) + yield "sum.label Sum of all counters" + yield "sum.type DERIVE" + yield "sum.graph no" + for index, rate in enumerate(sorted(all_rates, key=num_extract)): + details = all_rates[rate] + key = get_key(rate) + yield "{key}.label {rate_label}".format(key=key, rate_label=details["rate_label"]) + yield "{key}.type DERIVE".format(key=key) + yield "{key}.min 0".format(key=key) + if index < len(QUALITY_GRAPH_COLORS_16): + yield "{key}.colour {colour}".format(key=key, colour=QUALITY_GRAPH_COLORS_16[index]) + yield "{key}.draw {draw_type}".format(key=key, draw_type=("AREA" if is_first else "STACK")) + # divide the current value by the above sum of all counters and calculate percent + yield "{key}.cdef 100,{key},sum,/,*".format(key=key, cdef=cdef) + is_first = False + yield "" + + +class WifiInterface: + + def __init__(self, name, path, graph_base): + self._path = path + self._graph_base = graph_base + self.name = name + self.stations = tuple(self._parse_stations()) + + def _parse_arp_cache(self): + """ read IPs and MACs from /proc/net/arp and return a dictionary for MAC -> IP """ + arp_cache = {} + # example content: + # IP address HW type Flags HW address Mask Device + # 192.168.2.70 0x1 0x0 00:00:00:00:00:00 * eth0.10 + # 192.168.12.76 0x1 0x2 24:a4:3c:fd:76:98 * eth1.10 + for line in open("/proc/net/arp", "r").read().split("\n"): + # skip empty lines + if not line: continue + tokens = line.split() + ip, mac = tokens[0], tokens[3] + # the header line can be ignored - all other should have well-formed MACs + if not ":" in mac: continue + # ignore remote peers outside of the broadcast domain + if mac == "00:00:00:00:00:00": continue + arp_cache[mac] = ip + return arp_cache + + def _parse_stations(self): + stations_base = os.path.join(self._path, "stations") + arp_cache = self._parse_arp_cache() + for item in os.listdir(stations_base): + peer_mac = item + # use the IP or fall back to the MAC without separators (":") + if peer_mac in arp_cache: + label = arp_cache[peer_mac] + key = peer_mac.replace(":", "") + else: + label = peer_mac + key = "host_" + peer_mac.replace(":", "").replace(".", "") + yield Station(label, key, os.path.join(stations_base, item)) + + def get_config(self, scope): + yield from Station.get_summary_config(scope, self.stations, self._graph_base) + for station in self.stations: + yield from station.get_config(scope, self._graph_base) + yield "" + + def get_values(self, scope): + yield from Station.get_summary_values(scope, self.stations, self._graph_base) + for station in self.stations: + yield from station.get_values(scope, self._graph_base) + yield "" + + +class Ath9kDriver: + + def __init__(self, path, graph_base): + self._path = path + self._graph_base = graph_base + self.interfaces = tuple(self._parse_interfaces()) + + def _parse_interfaces(self): + for phy in os.listdir(self._path): + phy_path = os.path.join(self._path, phy) + for item in os.listdir(phy_path): + if item.startswith("netdev:"): + wifi = item.split(":", 1)[1] + label = "{phy}/{interface}".format(phy=phy, interface=wifi) + wifi_path = os.path.join(phy_path, item) + graph_base = "{base}_{phy}_{interface}".format(base=self._graph_base, phy=phy, interface=wifi) + yield WifiInterface(label, wifi_path, graph_base) + + def get_config(self, scope): + for interface in self.interfaces: + yield from interface.get_config(scope) + + def get_values(self, scope): + for interface in self.interfaces: + yield from interface.get_values(scope) + + + +def _get_up_down_pair(unit, key_up, key_down, factor=None, divider=None, use_negative=True): + """ return all required statements for a munin-specific up/down value pair + "factor" or "divider" can be given for unit conversions + """ + for key in (key_up, key_down): + if use_negative: + yield "{key}.label {unit}".format(key=key, unit=unit) + else: + yield "{key}.label {key} {unit}".format(key=key, unit=unit) + yield "{key}.type COUNTER".format(key=key) + if factor: + yield "{key}.cdef {key},{factor},*".format(key=key, factor=factor) + if divider: + yield "{key}.cdef {key},{divider},/".format(key=key, divider=divider) + if use_negative: + yield "{key_down}.graph no".format(key_down=key_down) + yield "{key_up}.negative {key_down}".format(key_up=key_up, key_down=key_down) + + +def get_scope(): + called_name = os.path.basename(sys.argv[0]) + name_prefix = "ath9k_" + if called_name.startswith(name_prefix): + scope = called_name[len(name_prefix):] + if not scope in PLUGIN_SCOPES: + print_error("Invalid scope requested: {0} (expected: {1})".format(scope, PLUGIN_SCOPES)) + sys.exit(2) + else: + print_error("Invalid filename - failed to discover plugin scope") + sys.exit(2) + return scope + + +def print_error(message): + # necessary fallback for micropython + linesep = getattr(os, "linesep", "\n") + sys.stderr.write(message + linesep) + + +if __name__ == "__main__": + ath9k = Ath9kDriver(SYS_BASE_DIR, GRAPH_BASE_NAME) + # parse arguments + if len(sys.argv) > 1: + if sys.argv[1]=="config": + for item in ath9k.get_config(get_scope()): + print(item) + sys.exit(0) + elif sys.argv[1] == "autoconf": + if os.path.exists(SYS_BASE_PATH): + print('yes') + else: + print('no') + sys.exit(0) + elif sys.argv[1] == "suggest": + for scope in PLUGIN_SCOPES: + print(scope) + sys.exit(0) + elif sys.argv[1] == "version": + print_error('olsrd Munin plugin, version %s' % plugin_version) + sys.exit(0) + elif sys.argv[1] == "": + # ignore + pass + else: + # unknown argument + print_error("Unknown argument") + sys.exit(1) + + # output values + for item in ath9k.get_values(get_scope()): + print(item) From 3509d2a6edf33dcb5004439b90ef6ba9730705d9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 30 Oct 2015 05:23:16 +0100 Subject: [PATCH 017/718] remove python 2 compatibility due to widespread use of "yield from" --- plugins/network/ath9k_ | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/network/ath9k_ b/plugins/network/ath9k_ index 81229123..7c20207c 100755 --- a/plugins/network/ath9k_ +++ b/plugins/network/ath9k_ @@ -11,7 +11,6 @@ # # # This plugin works with the following python interpreters: -# * Python 2 # * Python 3 # * micropython # @@ -51,7 +50,7 @@ if which micropython >/dev/null; then /usr/bin/micropython "$0" "$@" else - python "$0" "$@" + python3 "$0" "$@" fi exit $? """ From 14ff36a31c3bbbc001424d255b746e3e8664d82b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 30 Oct 2015 05:23:46 +0100 Subject: [PATCH 018/718] hide the python code part from shell parsers This works around a syntax warning of the continuous integration system. The cause of this warning could have never triggered anyway. This commit encloses the whole python code safely in a "here" document. --- plugins/network/ath9k_ | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/network/ath9k_ b/plugins/network/ath9k_ index 7c20207c..405b93c2 100755 --- a/plugins/network/ath9k_ +++ b/plugins/network/ath9k_ @@ -53,6 +53,10 @@ else python3 "$0" "$@" fi exit $? + +# For shell: ignore everything starting from here until the last line of this file. +# This is necessary for syntax checkers that try to complain about invalid shell syntax below. +true < Date: Fri, 30 Oct 2015 05:57:24 +0100 Subject: [PATCH 019/718] safely hide the python code part from shell parsers This works around a syntax warning emitted by the continuous integration tool. This commit encloses the python part of the hybrid shell / python script within a "here" document. --- plugins/network/olsrd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/network/olsrd b/plugins/network/olsrd index 23bdf02a..5fcfedba 100755 --- a/plugins/network/olsrd +++ b/plugins/network/olsrd @@ -61,6 +61,10 @@ else python "$0" "$@" fi exit $? + +# For shell: ignore everything starting from here until the last line of this file. +# This is necessary for syntax checkers that try to complain about invalid shell syntax below. +true < Date: Mon, 9 Nov 2015 21:40:16 -0300 Subject: [PATCH 020/718] memory_by_process: try to be smarted about labels and cleanup field names more deeply --- plugins/system/memory_by_process | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/system/memory_by_process b/plugins/system/memory_by_process index b06e3391..6af7dad4 100755 --- a/plugins/system/memory_by_process +++ b/plugins/system/memory_by_process @@ -30,18 +30,21 @@ if [ "$1" = "config" ]; then { @a = split; $proc = $a[10]; - $proc =~ s|.*/||; + $label = $proc; + $proc =~ s/ .*//; + if($proc =~ /^\[/) { $proc =~ s|/.*||; } else { $proc =~ s|.*/||; } $proc =~ s/:.*//; $proc =~ tr/[]//d; $proc =~ tr/A-Za-z0-9/_/c; $vsz = $a[4]; $total{$proc} += $vsz; + $labels{$proc} = $label; } my $stack = 0; sub draw() { return $stack++ ? "STACK" : "AREA" } print map { - "$_.label $_\n" . + "$_.label " . $labels{$_} . "\n" . "$_.min 0\n" . "$_.draw " . draw() . "\n" . "$_.cdef $_,1024,*\n" @@ -56,7 +59,7 @@ else { @a = split; $proc = $a[10]; - $proc =~ s|.*/||; + if($proc =~ /^\[/) { $proc =~ s|/.*||; } else { $proc =~ s|.*/||; } $proc =~ s/:.*//; $proc =~ tr/[]//d; $proc =~ tr/A-Za-z0-9/_/c; From 99a42cafd61f12be36fc70ab6cdd1c208c95589f Mon Sep 17 00:00:00 2001 From: Artem Sheremet Date: Wed, 19 Aug 2015 17:55:00 +0200 Subject: [PATCH 021/718] Add "process links" graph to ejabberd_resources --- .../ejabberd_resources_/ejabberd_resources_ | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/ejabberd/ejabberd_resources_/ejabberd_resources_ b/plugins/ejabberd/ejabberd_resources_/ejabberd_resources_ index 4621a5e7..e04b0090 100755 --- a/plugins/ejabberd/ejabberd_resources_/ejabberd_resources_ +++ b/plugins/ejabberd/ejabberd_resources_/ejabberd_resources_ @@ -1,7 +1,5 @@ #!/usr/bin/env bash -# ejabberd_resources_ revision 4 (Mar 2015) -# # Tested with ejabberd 2.1.x # # This plugin is capable to show: @@ -206,6 +204,20 @@ function ejabberd_report_mnesia_recs() { ejabberd_report_mnesia "$1" recs } +function ejabberd_report_process_links() { + ejabberd_exec " + lists:filtermap( + fun(Name) -> + case whereis(Name) of + Pid when is_pid(Pid) -> + {links, Links} = erlang:process_info(Pid, links), + {true, {Name, length(Links)}}; + _ -> + false + end, + registered())" +} + function open_files_counter_util() { if hash lsof &>/dev/null; then echo lsof @@ -281,6 +293,7 @@ case $1 in memory processes ports +process_links online_users registered_users mnesia_recs From 0bf9659577b3ed5be54759a79170b57317c7bba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=2ES=20=C2=AB=20Benpro=20=C2=BB?= Date: Thu, 14 Jan 2016 21:55:52 +0100 Subject: [PATCH 022/718] fr24 - Plugin to monitor your flightradar24.com feeder. --- plugins/fr24/fr24 | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 plugins/fr24/fr24 diff --git a/plugins/fr24/fr24 b/plugins/fr24/fr24 new file mode 100755 index 00000000..cef2ae55 --- /dev/null +++ b/plugins/fr24/fr24 @@ -0,0 +1,49 @@ +#!/bin/bash +# +: <<=cut + +=head1 NAME + +fr24 - Plugin to monitor your flightradar24.com feeder. + +=head1 APPLICABLE SYSTEMS + +All Linux systems. + +=head1 CONFIGURATION + +The following is default configuration: + + [fr24] + env.MONITOR http://192.168.1.1:8754/monitor.json + +Set the right URL according to your FR24 Feeder. + +=head1 VERSION + + 1 + +=head1 AUTHOR + +Benoît.S « Benpro » + +=head1 LICENSE + +WTFPL + +=cut + +MONITOR=${MONITOR:-http://192.168.1.1:8754/monitor.json} + +case $1 in + config) + echo "graph_title Number of planes in sight +graph_info Number of planes in sight with DVB-T receiver. +graph_category fr24 +graph_vlabel Number of planes +planes.label planes" + exit 0;; +esac + +planes=$(curl -qs $MONITOR | grep -Eo '"d11_map_size":"[0-9]+"' | grep -Eo '[0-9]+' | tail -1) +echo "planes.value $planes" From 381101d222c8bd65e9eab87c4568af2f4a8fe98c Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Thu, 14 Jan 2016 21:14:40 -0600 Subject: [PATCH 023/718] Ignore sfq child classes. Normal rate monitoring should be preformed on parent HTB classes/queues. SFQ classes are dynamically allocated (as queues fill) and given random names and this messes up charts. --- plugins/network/tc_ | 2 +- plugins/network/tc_drops_ | 2 +- plugins/network/tc_packets_ | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 1418222d..eadd5280 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -16,7 +16,7 @@ DEVICE=${0##*/tc_} mytc() { - tc -s class show dev $1 | tr "\n" "|" | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | sort -n + tc -s class show dev $1 | tr "\n" "|" | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n } case $1 in diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index fc89ab38..f93aa60e 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -16,7 +16,7 @@ DEVICE=${0##*/tc_drops_} mytc() { - tc -s class show dev $1 | tr "\n," "| " | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | sort -n + tc -s class show dev $1 | tr "\n," "| " | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n } case $1 in diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index 99382325..c64712a0 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -16,7 +16,7 @@ DEVICE=${0##*/tc_packets_} mytc() { - tc -s class show dev $1 | tr "\n," "| " | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | sort -n + tc -s class show dev $1 | tr "\n," "| " | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n } case $1 in From 0553d6c2d02be2e98a62fe8dc7874cce203ce4d2 Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Thu, 14 Jan 2016 21:18:59 -0600 Subject: [PATCH 024/718] Fix undef string compare. Guard against $type not defined. --- plugins/network/qos_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/network/qos_ b/plugins/network/qos_ index 3b9bc6fa..ccf6f763 100755 --- a/plugins/network/qos_ +++ b/plugins/network/qos_ @@ -81,7 +81,7 @@ my( $name, $id1, $id2, $type, $rate, $parent); for( my $x = 0; $x < scalar(@class); $x++ ) { if($class[$x] =~ m/^class/i ) { ( $name, $id1, $id2, $type, $parent) = ( $class[$x] =~ m/^class\s+(\w+)\s+(\d+):(\d+)\s+(\w+)\s([^\s]+)/i ); - if($type eq "parent") { + if($type && $type eq "parent") { $x++; ( $rate ) = ( $class[$x] =~ m/Sent\s+(\d+)\s+/i ); $handle = "$name"."${id1}_${id2}"; @@ -168,4 +168,4 @@ foreach my $key (sort by_handle keys %queues) { print $queues{$key}->{queue},$queues{$key}->{handle}, ".value ",$queues{$key}->{sent}, "\n"; } # -# vim:syntax=perl \ No newline at end of file +# vim:syntax=perl From 999f43474e767d6cd7d9c6326a59a4025749722f Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Thu, 14 Jan 2016 21:20:07 -0600 Subject: [PATCH 025/718] Ignore sfq child classes. Normal rate monitoring should be preformed on parent HTB classes/queues. SFQ classes are dynamically allocated (as queues fill) and given random names and this messes up charts. --- plugins/network/qos_ | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/network/qos_ b/plugins/network/qos_ index ccf6f763..b676bbb8 100755 --- a/plugins/network/qos_ +++ b/plugins/network/qos_ @@ -157,6 +157,7 @@ sub by_handle { } foreach my $key (sort by_handle keys %queues) { + next if $key =~ /sfq/i; $haschild = 0; foreach my $key2 (sort by_handle keys %queues) { if($queues{$key}->{id} eq $queues{$key2}->{parent}) { From 87efe7091bd7d08e1577cbf66d4028c847782248 Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Thu, 14 Jan 2016 21:36:14 -0600 Subject: [PATCH 026/718] Ignore sfq child classes for configure. --- plugins/network/qos_ | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/network/qos_ b/plugins/network/qos_ index b676bbb8..b2a13302 100755 --- a/plugins/network/qos_ +++ b/plugins/network/qos_ @@ -106,6 +106,8 @@ if ( exists $ARGV[0] and $ARGV[0] eq 'config' ) { print "graph_data_size $ENV{graph_data_size}\n" if $ENV{graph_data_size}; print "graph_order "; foreach my $key (sort by_handle keys %queues) { + delete $queues{$key} if $key =~ /sfq/i; + next if $key =~ /sfq/i; $haschild = 0; foreach my $key2 (sort by_handle keys %queues) { if($queues{$key}->{id} eq $queues{$key2}->{parent}) { From 74aa84d51a798e682de0848116d3b53c11692811 Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Thu, 14 Jan 2016 22:59:15 -0600 Subject: [PATCH 027/718] Remove unneeded sort Parsed data is already sorted. --- plugins/network/tc_ | 2 +- plugins/network/tc_drops_ | 2 +- plugins/network/tc_packets_ | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index eadd5280..46c38b65 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -43,7 +43,7 @@ case $1 in ;; config) - echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 }' | sort -n | tr "\n" " "`" + echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 }' | tr "\n" " "`" echo "graph_title $DEVICE TC traffic" echo 'graph_args --base 1000' echo 'graph_vlabel bits per ${graph_period}' diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index f93aa60e..72d088e0 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -43,7 +43,7 @@ case $1 in ;; config) - echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_drops" }' | sort -n | tr "\n" " "`" + echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_drops" }' | tr "\n" " "`" echo "graph_title $DEVICE TC traffic drops" echo 'graph_args --base 1000' echo 'graph_vlabel bits per ${graph_period}' diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index c64712a0..fc08bafa 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -43,7 +43,7 @@ case $1 in ;; config) - echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_packets" }' | sort -n | tr "\n" " "`" + echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_packets" }' | tr "\n" " "`" echo "graph_title $DEVICE TC traffic packets" echo 'graph_args --base 1000' echo 'graph_vlabel bits per ${graph_period}' From b47505cef2e3cfd524e1b5647e76ec87e1966c1e Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Fri, 15 Jan 2016 00:45:54 -0600 Subject: [PATCH 028/718] Correct graph_vlabel to data type. --- plugins/network/tc_drops_ | 2 +- plugins/network/tc_packets_ | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index 72d088e0..d4b5719b 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -46,7 +46,7 @@ case $1 in echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_drops" }' | tr "\n" " "`" echo "graph_title $DEVICE TC traffic drops" echo 'graph_args --base 1000' - echo 'graph_vlabel bits per ${graph_period}' + echo 'graph_vlabel drops per ${graph_period}' echo 'graph_category network' echo "graph_info This graph shows the TC classes traffic drops of the $DEVICE network interface, epxressed in packets." diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index fc08bafa..31aec7cf 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -46,7 +46,7 @@ case $1 in echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_packets" }' | tr "\n" " "`" echo "graph_title $DEVICE TC traffic packets" echo 'graph_args --base 1000' - echo 'graph_vlabel bits per ${graph_period}' + echo 'graph_vlabel packets per ${graph_period}' echo 'graph_category network' echo "graph_info This graph shows the TC classes traffic packets of the $DEVICE network interface." From 6b94ef028424e6ae0d9bd80b99f94d8251ba6067 Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Sun, 17 Jan 2016 16:31:35 -0600 Subject: [PATCH 029/718] Change to DERIVE. Values can return to 0 on service restart which COUNTER data store will not handle well. --- plugins/network/qos_ | 2 +- plugins/network/tc_ | 2 +- plugins/network/tc_drops_ | 2 +- plugins/network/tc_packets_ | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/network/qos_ b/plugins/network/qos_ index b2a13302..06592904 100755 --- a/plugins/network/qos_ +++ b/plugins/network/qos_ @@ -138,7 +138,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq 'config' ) { } elsif (exists $ENV{"max$queues{$key}->{handle}"}) { print $queues{$key}->{queue},$queues{$key}->{handle}, ".max ",$ENV{"max$queues{$key}->{handle}"},"\n"; } - print $queues{$key}->{queue},$queues{$key}->{handle}, ".type COUNTER\n"; + print $queues{$key}->{queue},$queues{$key}->{handle}, ".type DERIVE\n"; if($count == 0){ print $queues{$key}->{queue},$queues{$key}->{handle}, ".draw AREA\n"; } else { diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 46c38b65..7b29c7f7 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -50,7 +50,7 @@ case $1 in echo 'graph_category network' echo "graph_info This graph shows the TC classes traffic of the $DEVICE network interface. Please note that the traffic is shown in bits per second, not bytes." - mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 ".type COUNTER\n" $2 "_" $3 "_" $4 ".min 0\n" $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*" }' + mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 ".type DERIVE\n" $2 "_" $3 "_" $4 ".min 0\n" $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*" }' exit 0 ;; esac diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index d4b5719b..f719291b 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -51,7 +51,7 @@ case $1 in echo "graph_info This graph shows the TC classes traffic drops of the $DEVICE network interface, epxressed in packets." # mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_drops.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_drops.type COUNTER\n" $2 "_" $3 "_" $4 "_drops.min 0\n" $2 "_" $3 "_" $4 "_drops.cdef " $2 "_" $3 "_" $4 ",8,*" }' - mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_drops.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_drops.type COUNTER\n" $2 "_" $3 "_" $4 "_drops.min 0" }' + mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_drops.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_drops.type DERIVE\n" $2 "_" $3 "_" $4 "_drops.min 0" }' exit 0 ;; esac diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index 31aec7cf..8ae58204 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -51,7 +51,7 @@ case $1 in echo "graph_info This graph shows the TC classes traffic packets of the $DEVICE network interface." # mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_packets.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_packets.type COUNTER\n" $2 "_" $3 "_" $4 "_packets.min 0\n" $2 "_" $3 "_" $4 "_packets.cdef " $2 "_" $3 "_" $4 ",8,*" }' - mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_packets.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_packets.type COUNTER\n" $2 "_" $3 "_" $4 "_packets.min 0" }' + mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_packets.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_packets.type DERIVE\n" $2 "_" $3 "_" $4 "_packets.min 0" }' exit 0 ;; esac From e5260be4364e4700c0584dd45a48b5f17ac6a48a Mon Sep 17 00:00:00 2001 From: Alexander Shurakov Date: Tue, 26 Jan 2016 15:28:23 +0300 Subject: [PATCH 030/718] dovecot plugin check log file type by name If dovecot plugin found dovecot-only log it will analyze it without filtering by 'dovecot' tag --- plugins/dovecot/dovecot | 43 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/plugins/dovecot/dovecot b/plugins/dovecot/dovecot index c288a44b..63ed2325 100644 --- a/plugins/dovecot/dovecot +++ b/plugins/dovecot/dovecot @@ -21,8 +21,14 @@ $aborted = 0; ($dirname = $0) =~ s/[^\/]+$//; +$dovelogfile = 0 ; + $logfile = $ENV{'LOGFILE'} || '/var/log/mail.log'; +if ( $logfile =~ /dovecot/ ) { + $dovelogfile = 1 ; +} + # Use an overrided $PATH for all external programs if needed $DOVEADM = "doveadm"; @@ -171,6 +177,10 @@ if (!defined $pos) { $pos = $startsize; } +print "# Logfile: $logfile\n"; +print "# Pos: $pos\n"; +print "# Startsize: $startsize\n"; + if ($startsize < $pos) { # Log rotated parseDovecotfile ($rotlogfile, $pos, (stat $rotlogfile)[7]); @@ -208,25 +218,26 @@ sub parseDovecotfile { my $line =; chomp ($line); - if ($line !~ m/dovecot/) { next; } + if ( $dovelogfile == 0 and $line !~ m/dovecot/) { next; } + else { + if ($line =~ m/Aborted/) { + $aborted++; - if ($line =~ m/Aborted/) { - $aborted++; + } elsif ($line =~ m/Login:/) { + $login++; - } elsif ($line =~ m/Login:/) { - $login++; + if ( $line =~ m/TLS/) { + $tls++; + } elsif ($line =~ m/SSL/) { + $ssl++; + } - if ( $line =~ m/TLS/) { - $tls++; - } elsif ($line =~ m/SSL/) { - $ssl++; - } - - if ( $line =~ m/pop3-login:/) { - $pop3login++; - } elsif ($line =~ m/imap-login:/) { - $imaplogin++; - } + if ( $line =~ m/pop3-login:/) { + $pop3login++; + } elsif ($line =~ m/imap-login:/) { + $imaplogin++; + } + } } } close(logf); From a3a7008904e1f7b5e70d7a986a4855f4a301eeaf Mon Sep 17 00:00:00 2001 From: Alexander Shurakov Date: Tue, 26 Jan 2016 15:32:33 +0300 Subject: [PATCH 031/718] Update dovecot Remove debug output --- plugins/dovecot/dovecot | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/dovecot/dovecot b/plugins/dovecot/dovecot index 63ed2325..af73b414 100644 --- a/plugins/dovecot/dovecot +++ b/plugins/dovecot/dovecot @@ -177,10 +177,6 @@ if (!defined $pos) { $pos = $startsize; } -print "# Logfile: $logfile\n"; -print "# Pos: $pos\n"; -print "# Startsize: $startsize\n"; - if ($startsize < $pos) { # Log rotated parseDovecotfile ($rotlogfile, $pos, (stat $rotlogfile)[7]); From 26cf15aaddd50ed2dda1dd5fa30040691c6efdc2 Mon Sep 17 00:00:00 2001 From: Jonas Palm Date: Sun, 7 Feb 2016 16:09:52 +0100 Subject: [PATCH 032/718] Wordpress Multisite Plugin Monitors total instances, users, posts, comments and pingbacks for the multisite installation and also posts, comments and pingbacks for every multisite instance. --- plugins/other/wordpress-multisite | 126 ++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 plugins/other/wordpress-multisite diff --git a/plugins/other/wordpress-multisite b/plugins/other/wordpress-multisite new file mode 100644 index 00000000..fac95ec4 --- /dev/null +++ b/plugins/other/wordpress-multisite @@ -0,0 +1,126 @@ +#!/bin/bash +# wordpress-multisite plugin +# +# Author Jonas Palm +# Version 0.1 +# Date 2016-02-07 +# +: <<=cut +=head1 NAME +Wordpress-Multisite Munin Plugin + +A Munin plugin to monitor posts, comments and pingbacks from every multisite instance. +It uses multigraphs and also shows the combined number of posts, comments, pingbacks, instances and users. + +Most database requests came from the wordpress-mu-plugin which was written by Andre Darafarin and Chris Bair + +=head1 CONFIGURATION +The plugin need access to the wordpress database + +=head2 Config file +Create the config file plugin-conf.d/wordpress with the following values: + +=over 5 +=item * [wordpress*] +=item * env.DB_USER +=item * env.DB_PASSWORD +=item * env.DB_NAME +=item * env.DB_PREFIX +=item * env.DB_HOST + +=back + +=head1 VERSION + +0.1 2016-02-07 + +=head1 AUTHOR + +Jonas Palm + +=cut + +# Fill some variables +DB_USER=${DB_USER} +DB_PASSWORD=${DB_PASSWORD} +DB_NAME=${DB_NAME:-wordpress} +DB_PREFIX=${DB_PREFIX:-wp_} +DB_HOST=${DB_HOST:-localhost} +DB_PORT=${DB_PORT:-3306} + +MYSQLOPTS="-h$DB_HOST -P $DB_PORT -p$DB_PASSWORD -u$DB_USER -D $DB_NAME --column-names=0 -s" + +if [ "$1" = "config" ]; then + echo "multigraph wordpress" + echo "graph_title Wordpress Mulitsite" + echo "graph_order instances users posts comments pingbacks" + echo "graph_vlabel Wordpress" + echo "graph_category Wordpress" + echo "graph_info Some Statistics of Wordpress" + echo "instances.label Instances" + echo "users.label Users" + echo "posts.label Posts" + echo "comments.label Comments" + echo "pingbacks.label Pingbacks" +else + CNT=0 + for n in `mysql $MYSQLOPTS --execute="select blog_id from ${DB_PREFIX}blogs"`; do + if [ "$n" == "1" ]; then + i= + else + i=${n}_ + fi + + POSTS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}posts WHERE post_status = 'publish' AND post_password = '' AND post_type = 'post';"` + (( POSTS_ += POSTS )) + + COMMENTS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}comments WHERE comment_approved = '1' AND comment_type = '';"` + (( COMMENTS_ += COMMENTS )) + + PINGBACKS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}comments WHERE comment_approved = '1' AND comment_type = 'pingback';"` + (( PINGBACKS_ += PINGBACKS )) + + (( CNT += 1 )) + done + + # return values + echo "multigraph wordpress" + echo "posts.value $POSTS_" + echo "comments.value $COMMENTS_" + echo "pingbacks.value $PINGBACKS_" + echo "instances.value $CNT" + echo "users.value `mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}users ;"`" +fi + +# single blogs +for n in `mysql $MYSQLOPTS --execute="select blog_id from ${DB_PREFIX}blogs"`; do + if [ "${n}" == "1" ]; then + i= + else + i=${n}_ + fi + + if [ "$n" -le "9" ]; then + n=0${n} + fi + + if [ "$1" = "config" ]; then + echo "multigraph wordpress.site_${n}" + echo "graph_title `mysql $MYSQLOPTS --execute=\"select option_value from ${DB_PREFIX}${i}options where option_name = 'siteurl';\"`" + echo "graph_order posts comments pingbacks" + echo "graph_vlabel Wordpress ID ${n}" + echo "posts.label Posts" + echo "comments.label Comments" + echo "pingbacks.label Pingbacks" + else + POSTS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}posts WHERE post_status = 'publish' AND post_password = '' AND post_type = 'post';"` + COMMENTS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}comments WHERE comment_approved = '1' AND comment_type = '';"` + PINGBACKS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}comments WHERE comment_approved = '1' AND comment_type = 'pingback';"` + + # return values + echo "multigraph wordpress.site_${n}" + echo "posts.value $POSTS" + echo "comments.value $COMMENTS" + echo "pingbacks.value $PINGBACKS" + fi +done From feb54309caccae115156f237d66e1be3639acf33 Mon Sep 17 00:00:00 2001 From: Jonas Palm Date: Sun, 7 Feb 2016 16:15:02 +0100 Subject: [PATCH 033/718] Added Option Description for env.DB_PORT --- plugins/other/wordpress-multisite | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/other/wordpress-multisite b/plugins/other/wordpress-multisite index fac95ec4..126d46b9 100644 --- a/plugins/other/wordpress-multisite +++ b/plugins/other/wordpress-multisite @@ -2,6 +2,7 @@ # wordpress-multisite plugin # # Author Jonas Palm +# E-Mail jonaspalm . posteo. de # Version 0.1 # Date 2016-02-07 # @@ -20,13 +21,14 @@ The plugin need access to the wordpress database =head2 Config file Create the config file plugin-conf.d/wordpress with the following values: -=over 5 +=over 6 =item * [wordpress*] =item * env.DB_USER =item * env.DB_PASSWORD =item * env.DB_NAME =item * env.DB_PREFIX =item * env.DB_HOST +=item * env.DB_PORT =back From cd68f192d52d90b0c5a2ac8c7f9271b561b258ad Mon Sep 17 00:00:00 2001 From: Vincas Dargis Date: Thu, 11 Feb 2016 15:32:08 +0200 Subject: [PATCH 034/718] pgbouncer_: allow to separate plugin name and pool name --- plugins/postgresql/pgbouncer_ | 37 ++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/plugins/postgresql/pgbouncer_ b/plugins/postgresql/pgbouncer_ index 91aeef66..cd55fd5a 100755 --- a/plugins/postgresql/pgbouncer_ +++ b/plugins/postgresql/pgbouncer_ @@ -16,11 +16,17 @@ my $db_user = $ENV{'pgbouncer_user'} || 'postgres'; my $db_port = $ENV{'pgbouncer_port'} || '6432'; my $db_host = $ENV{'pgbouncer_host'} || 'localhost'; my $db_pass = $ENV{'pgbouncer_pass'} || ''; +my $db_pool = $ENV{'pgbouncer_pool'} || ''; my $db_name = 'pgbouncer'; my @data = (); # get the DB (pool) name we want to fetch $plugin_name =~ /pgbouncer_(.*)$/; -my $pool_name = $1; +my $plugin_suffix = $1; +#if pool name is specified explicitly in config file +#use plugin name together with pool name in graph title: +my $pool_name = ($db_pool) ? $db_pool : $plugin_suffix; +my $plugin_title = ($db_pool) ? $plugin_suffix." ".$pool_name : $pool_name; + # bail if no name if (!$pool_name) { @@ -52,8 +58,9 @@ if (defined($ARGV[0])) { # create the basic RRD # stats: average connections + print "multigraph ".$plugin_name."_stats_avg_req\n"; - print "graph_title PgBouncer $pool_name average connections\n"; + print "graph_title PgBouncer $plugin_title average connections\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Average connections\n"; print "graph_scale no\n"; # so we do not print "micro, milli, kilo, etc" @@ -64,7 +71,7 @@ if (defined($ARGV[0])) print $pool_name."_avg_req.draw LINE2\n"; # stats: average time for query print "multigraph ".$plugin_name."_stats_avg_query\n"; - print "graph_title PgBouncer $pool_name average query time\n"; + print "graph_title PgBouncer $plugin_title average query time\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Average time per query (microseconds)\n"; print "graph_category pgbouncer\n"; @@ -74,7 +81,7 @@ if (defined($ARGV[0])) print $pool_name."_avg_query.draw LINE2\n"; # stats: in/out bytes print "multigraph ".$plugin_name."_stats_bytesinout\n"; - print "graph_title PgBouncer $pool_name average bytes received/sent\n"; + print "graph_title PgBouncer $plugin_title average bytes received/sent\n"; print "graph_args --base 1024\n"; # numbers in bytes print "graph_vlabel Average bytes received (-)/sent (+)\n"; print "graph_category pgbouncer\n"; @@ -92,7 +99,7 @@ if (defined($ARGV[0])) print $pool_name."_avg_sent.negative ".$pool_name."_avg_recv\n"; # pools: server (sv_) print "multigraph ".$plugin_name."_pools_server\n"; - print "graph_title PgBouncer $pool_name servers\n"; + print "graph_title PgBouncer $plugin_title servers\n"; print "graph_category pgbouncer\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Server connections\n"; @@ -124,7 +131,7 @@ if (defined($ARGV[0])) print $pool_name."_server_login.draw STACK\n"; # pools: client (cl_) print "multigraph ".$plugin_name."_pools_client\n"; - print "graph_title PgBouncer $pool_name clients\n"; + print "graph_title PgBouncer $plugin_title clients\n"; print "graph_category pgbouncer\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Client connections\n"; @@ -141,7 +148,7 @@ if (defined($ARGV[0])) print $pool_name."_client_waiting.draw STACK\n"; # pools: maxwait (longest waiting connection, should be 0) print "multigraph ".$plugin_name."_pools_maxwait\n"; - print "graph_title PgBouncer $pool_name maximum waiting time\n"; + print "graph_title PgBouncer $plugin_title maximum waiting time\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Maximum wait time (seconds)\n"; print "graph_category pgbouncer\n"; @@ -219,6 +226,7 @@ perl and DBD::Pg is required, and pgbouncer must been installed with a correct s =head1 CONFIGURATION the plugin that will be run needs to have the pool name after the plugin base name. +alternatively, pool name can be specified in config file as env.pgbouncer_pool option, separating plugin name from pool name. =head2 plugin configuration @@ -241,6 +249,21 @@ more extended would be: env.pgbouncer_port 6542 env.pgbouncer_host localhost +another example, where different pgbouncers (and so munin plugins) connecting to same db: + [pgbouncer_weblogin] + env.pgbouncer_pass barfoo + env.pgbouncer_user bar + env.pgbouncer_port 6542 + env.pgbouncer_host localhost + env.pgbouncer_pool dbname + + [pgbouncer_webmain] + env.pgbouncer_pass barfoo + env.pgbouncer_user bar + env.pgbouncer_port 6543 + env.pgbouncer_host localhost + env.pgbouncer_pool dbname + The database name is always pgbouncer =head1 OUTPUT From 1733fee7f82ef8ad225bd59a2419fde60ac0f9e6 Mon Sep 17 00:00:00 2001 From: kuzetsa Date: Thu, 25 Feb 2016 04:06:56 -0500 Subject: [PATCH 035/718] heading on .png didn't match name of plugin packets are a specific type of traffic --- plugins/time/ntp_packets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/time/ntp_packets b/plugins/time/ntp_packets index 9b7d653d..ef21e610 100755 --- a/plugins/time/ntp_packets +++ b/plugins/time/ntp_packets @@ -32,7 +32,7 @@ import subprocess import sys if len(sys.argv) == 2 and sys.argv[1] == 'config': - print('graph_title NTP traffic') + print('graph_title NTP packets') print('graph_vlabel Packets/${graph_period} received(-)/sent(+)') print('graph_info This graph shows the packet rates of this ntpd. Ignored and dropped packets are graphed as positive values.') print('graph_category time') From 415d95252508b829e3d222b0f8a603cff0e68463 Mon Sep 17 00:00:00 2001 From: Sarah White Date: Thu, 25 Feb 2016 04:13:46 -0500 Subject: [PATCH 036/718] backported to work with python 2.7.10 https://wiki.python.org/moin/3to2 --- plugins/time/ntp_packets | 60 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/plugins/time/ntp_packets b/plugins/time/ntp_packets index ef21e610..30f3f1ae 100755 --- a/plugins/time/ntp_packets +++ b/plugins/time/ntp_packets @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # -*- python -*- # This plugin graphs the rate of sent, received, ignored, and dropped @@ -31,48 +31,48 @@ import os import subprocess import sys -if len(sys.argv) == 2 and sys.argv[1] == 'config': - print('graph_title NTP packets') - print('graph_vlabel Packets/${graph_period} received(-)/sent(+)') - print('graph_info This graph shows the packet rates of this ntpd. Ignored and dropped packets are graphed as positive values.') - print('graph_category time') - print('received.label Received') - print('received.type DERIVE') - print('received.graph no') - print('received.min 0') - print('sent.label Rx/Tx') - print('sent.type DERIVE') - print('sent.negative received') - print('sent.min 0') - print('dropped.label Dropped') - print('dropped.type DERIVE') - print('dropped.min 0') - print('ignored.label Ignored') - print('ignored.type DERIVE') - print('ignored.min 0') +if len(sys.argv) == 2 and sys.argv[1] == u'config': + print u'graph_title NTP packets' + print u'graph_vlabel Packets/${graph_period} received(-)/sent(+)' + print u'graph_info This graph shows the packet rates of this ntpd. Ignored and dropped packets are graphed as positive values.' + print u'graph_category time' + print u'received.label Received' + print u'received.type DERIVE' + print u'received.graph no' + print u'received.min 0' + print u'sent.label Rx/Tx' + print u'sent.type DERIVE' + print u'sent.negative received' + print u'sent.min 0' + print u'dropped.label Dropped' + print u'dropped.type DERIVE' + print u'dropped.min 0' + print u'ignored.label Ignored' + print u'ignored.type DERIVE' + print u'ignored.min 0' sys.exit(0) -os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:' + os.environ['PATH'] +os.environ[u'PATH'] = u'/usr/local/sbin:/usr/local/bin:' + os.environ[u'PATH'] # Assuming that the ntpd version is the same as the ntpq or ntpdc # version. This is how a proper install should be. -version = subprocess.check_output(['ntpq', '-c', 'version'], universal_newlines=True).split()[1][0:5].replace('.', '') +version = subprocess.check_output([u'ntpq', u'-c', u'version'], universal_newlines=True).split()[1][0:5].replace(u'.', u'') if int(version) >= 427: - cmd = 'ntpq' + cmd = u'ntpq' else: - cmd = 'ntpdc' + cmd = u'ntpdc' iostats = dict() -iostats_output = subprocess.check_output([cmd, '-c', 'iostats'], universal_newlines=True).splitlines() +iostats_output = subprocess.check_output([cmd, u'-c', u'iostats'], universal_newlines=True).splitlines() -for line in iostats_output: iostats[line.split(':')[0]] = int(line.split(':')[1]) +for line in iostats_output: iostats[line.split(u':')[0]] = int(line.split(u':')[1]) -print('received.value ' + str(iostats['received packets'])) -print('sent.value ' + str(iostats['packets sent'])) -print('dropped.value ' + str(iostats['dropped packets'])) -print('ignored.value ' + str(iostats['ignored packets'])) +print u'received.value ' + unicode(iostats[u'received packets']) +print u'sent.value ' + unicode(iostats[u'packets sent']) +print u'dropped.value ' + unicode(iostats[u'dropped packets']) +print u'ignored.value ' + unicode(iostats[u'ignored packets']) sys.exit(0) From 1c6af7ae05de43b0c080ff2ae5973e8c9c102526 Mon Sep 17 00:00:00 2001 From: Sarah White Date: Thu, 25 Feb 2016 04:22:22 -0500 Subject: [PATCH 037/718] replaced unicode character with [c] not sure how else to fix the error? --- plugins/time/ntp_packets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/time/ntp_packets b/plugins/time/ntp_packets index 30f3f1ae..e237446c 100755 --- a/plugins/time/ntp_packets +++ b/plugins/time/ntp_packets @@ -13,7 +13,7 @@ # Symlink this plugin into the node's plugins directory (like # /etc/munin/plugins). # -# Copyright © 2013 Kenyon Ralph +# Copyright [c] 2013 Kenyon Ralph # # This program is free software. It comes without any warranty, to the # extent permitted by applicable law. You can redistribute it and/or From 405f43277ca2a474467fdaeb08bcb6c6f6b7c6f2 Mon Sep 17 00:00:00 2001 From: ak4t0sh Date: Thu, 14 Apr 2016 21:17:48 +0200 Subject: [PATCH 038/718] remove extension from filename --- plugins/moodle/modules/{moodle_mod_chat.php => moodle_mod_chat} | 0 plugins/moodle/modules/{moodle_mod_forum.php => moodle_mod_forum} | 0 plugins/moodle/modules/{moodle_mod_quiz.php => moodle_mod_quiz} | 0 plugins/moodle/{moodle_files.php => moodle_files} | 0 plugins/moodle/{moodle_logs.php => moodle_logs} | 0 plugins/moodle/{moodle_modules_total.php => moodle_modules_total} | 0 plugins/moodle/{moodle_users_online.php => moodle_users_online} | 0 plugins/moodle/{moodle_users_total.php => moodle_users_total} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename plugins/moodle/modules/{moodle_mod_chat.php => moodle_mod_chat} (100%) rename plugins/moodle/modules/{moodle_mod_forum.php => moodle_mod_forum} (100%) rename plugins/moodle/modules/{moodle_mod_quiz.php => moodle_mod_quiz} (100%) rename plugins/moodle/{moodle_files.php => moodle_files} (100%) rename plugins/moodle/{moodle_logs.php => moodle_logs} (100%) rename plugins/moodle/{moodle_modules_total.php => moodle_modules_total} (100%) rename plugins/moodle/{moodle_users_online.php => moodle_users_online} (100%) rename plugins/moodle/{moodle_users_total.php => moodle_users_total} (100%) diff --git a/plugins/moodle/modules/moodle_mod_chat.php b/plugins/moodle/modules/moodle_mod_chat similarity index 100% rename from plugins/moodle/modules/moodle_mod_chat.php rename to plugins/moodle/modules/moodle_mod_chat diff --git a/plugins/moodle/modules/moodle_mod_forum.php b/plugins/moodle/modules/moodle_mod_forum similarity index 100% rename from plugins/moodle/modules/moodle_mod_forum.php rename to plugins/moodle/modules/moodle_mod_forum diff --git a/plugins/moodle/modules/moodle_mod_quiz.php b/plugins/moodle/modules/moodle_mod_quiz similarity index 100% rename from plugins/moodle/modules/moodle_mod_quiz.php rename to plugins/moodle/modules/moodle_mod_quiz diff --git a/plugins/moodle/moodle_files.php b/plugins/moodle/moodle_files similarity index 100% rename from plugins/moodle/moodle_files.php rename to plugins/moodle/moodle_files diff --git a/plugins/moodle/moodle_logs.php b/plugins/moodle/moodle_logs similarity index 100% rename from plugins/moodle/moodle_logs.php rename to plugins/moodle/moodle_logs diff --git a/plugins/moodle/moodle_modules_total.php b/plugins/moodle/moodle_modules_total similarity index 100% rename from plugins/moodle/moodle_modules_total.php rename to plugins/moodle/moodle_modules_total diff --git a/plugins/moodle/moodle_users_online.php b/plugins/moodle/moodle_users_online similarity index 100% rename from plugins/moodle/moodle_users_online.php rename to plugins/moodle/moodle_users_online diff --git a/plugins/moodle/moodle_users_total.php b/plugins/moodle/moodle_users_total similarity index 100% rename from plugins/moodle/moodle_users_total.php rename to plugins/moodle/moodle_users_total From 36588f9275b87ba8d5b83380d2ba8df91b814b74 Mon Sep 17 00:00:00 2001 From: Andi Nitshce Date: Sat, 16 Apr 2016 09:03:16 +0200 Subject: [PATCH 039/718] fixed naming for multiple GPUs in amd_gpu_; adjusted lower limit for temperature to 20 --- plugins/gpu/amd_gpu_ | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/gpu/amd_gpu_ b/plugins/gpu/amd_gpu_ index 4f7095e0..bdfe54d7 100755 --- a/plugins/gpu/amd_gpu_ +++ b/plugins/gpu/amd_gpu_ @@ -109,14 +109,14 @@ if [ "$1" = "config" ]; then case $name in temp) echo 'graph_title GPU temperature' - echo 'graph_args -l 0 -u 120' + echo 'graph_args -l 20 -u 120' echo 'graph_vlabel Degrees (C)' echo 'graph_category gpu' echo "graph_info Temperature information for AMD GPUs" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` echo "temp${nGpusCounter}.warning ${warning:-75}" echo "temp${nGpusCounter}.critical ${critical:-95}" echo "temp${nGpusCounter}.info Temperature information for $gpuName" @@ -143,7 +143,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` echo "memclock${nGpusCounter}.info Memory clock information for $gpuName" echo "memclock${nGpusCounter}.label Memory clock ($gpuName)" echo "coreclock${nGpusCounter}.info Core clock information for $gpuName" @@ -160,7 +160,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` echo "fan${nGpusCounter}.info Fan speed information for $gpuName" echo "fan${nGpusCounter}.label Fan speed ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) @@ -175,7 +175,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` echo "load${nGpusCounter}.info Load information for $gpuName" echo "load${nGpusCounter}.label Load ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) @@ -189,7 +189,7 @@ if [ "$1" = "config" ]; then nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]\ //'` echo "vcore${nGpusCounter}.info Vcore information for $gpuName" echo "vcore${nGpusCounter}.label Core voltage ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) From 187545eabec9ee5250a98c14056d9438add65f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoit=2ES=20=C2=AB=C2=A0Benpro=C2=A0=C2=BB?= Date: Sat, 30 Apr 2016 17:23:23 +0200 Subject: [PATCH 040/718] Added double quote to prevent globbing and word splitting --- plugins/fr24/fr24 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/fr24/fr24 b/plugins/fr24/fr24 index cef2ae55..6e93266b 100755 --- a/plugins/fr24/fr24 +++ b/plugins/fr24/fr24 @@ -45,5 +45,5 @@ planes.label planes" exit 0;; esac -planes=$(curl -qs $MONITOR | grep -Eo '"d11_map_size":"[0-9]+"' | grep -Eo '[0-9]+' | tail -1) +planes=$(curl -qs "$MONITOR" | grep -Eo '"d11_map_size":"[0-9]+"' | grep -Eo '[0-9]+' | tail -1) echo "planes.value $planes" From 82edf596896ddcec0e0d75af87790614cfa1f5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 14 May 2016 13:23:00 +0000 Subject: [PATCH 041/718] Added munin_events plugin --- plugins/munin/munin_events | 142 +++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100755 plugins/munin/munin_events diff --git a/plugins/munin/munin_events b/plugins/munin/munin_events new file mode 100755 index 00000000..121f1695 --- /dev/null +++ b/plugins/munin/munin_events @@ -0,0 +1,142 @@ +#!/bin/bash +# -*- sh -*- +: <<=cut + +=head1 NAME + +munin_events - Plugin to monitor munin updates + +=head1 APPLICABLE SYSTEMS + +All systems with "bash", "logtail" and "munin" + +=head1 CONFIGURATION + +The following is the default configuration + + [munin_events] + user munin + env.muninupdate /var/log/munin/munin-update.log + env.logtail2 /usr/sbin/logtail2 + +You could trigger alerts on update failures + + [munin_events] + env.munin_fatal_critical 0 + env.munin_error_critical 0 + env.munin_warning_warning 0 + env.munin_warning_critical 5 + +=head1 INTERPRETATION + +This plugin shows a graph with one line per munin state: +INFO, WARNING, ERROR, FATAL. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + + 1.2.20160514 + +=head1 AUTHOR + +Viktor Szépe + +=head1 LICENSE + +GPLv2 + +=cut + + +############################## +# Includes + +. $MUNIN_LIBDIR/plugins/plugin.sh + +############################## +# Configurable variables +muninupdate=${muninupdate:-/var/log/munin/munin-update.log} +logtail_bin=${logtail_bin:-/usr/sbin/logtail2} + +############################## +# Functions + +# Print one value +do_value() { + local FIELD="$1" + local EVENT_LABEL="$2" + local EVENT_COUNT + + EVENT_COUNT="$("$logtail_bin" -t "$muninupdate" 2> /dev/null | grep -c "^[0-9/: ]\{19\} \[${EVENT_LABEL}\]")" + if ! [ -z "${EVENT_COUNT//[0-9]/}" ]; then + echo "Cannot determine event count" 1>&2 + exit 10 + fi + + echo "${FIELD}.value ${EVENT_COUNT}" +} + +# Print the munin values +values() { + do_value 'munin_info' 'INFO' + do_value 'munin_warning' 'WARNING' + do_value 'munin_error' 'ERROR' + do_value 'munin_fatal' 'FATAL' + # Set offset + "$logtail_bin" "$muninupdate" &> /dev/null + chmod 640 "${muninupdate}.offset" +} + +# Print the munin config +config() { + echo 'graph_title Munin update events groupped by log levels' + echo 'graph_info This graph shows INFO, WARNING, ERROR and FATAL events' + echo 'graph_category munin' + echo 'graph_vlabel Number of events' + + echo 'graph_args --base 1000 -l 0' + echo 'graph_total total' + echo 'graph_printf %6.0lf' + + echo 'munin_info.label INFO' + print_warning munin_info + print_critical munin_info + echo 'munin_warning.label WARNING' + print_warning munin_warning + print_critical munin_warning + echo 'munin_error.label ERROR' + print_warning munin_error + print_critical munin_error + echo 'munin_fatal.label FATAL' + print_warning munin_fatal + print_critical munin_fatal +} + +# Print autoconfiguration hint +autoconf() { + if [ -r "${muninupdate}" ] && [ -x "$logtail_bin" ]; then + echo "yes" + else + echo "missing (${muninupdate} or (${logtail_bin})" + fi + exit +} + +############################## +# Main + +case "$1" in + config) + config + ;; + autoconf) + autoconf + ;; + *) + values + ;; +esac From b149f0cf63c83c7de7627fa287354d8cf0236ab1 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sun, 15 May 2016 17:38:38 +0200 Subject: [PATCH 042/718] disk/raid-mismatch-count != 0 is a critical alert --- plugins/disk/raid-mismatch-count | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/disk/raid-mismatch-count b/plugins/disk/raid-mismatch-count index b53dc9c8..bf4a89a7 100755 --- a/plugins/disk/raid-mismatch-count +++ b/plugins/disk/raid-mismatch-count @@ -50,6 +50,7 @@ graph_vlabel Count __EOF__ for target in $targets; do echo "$target.label $target" + echo "$target.critical 1" done exit fi From 370e1d05c3e6e970062eda40d5c3d7d9a15a2e17 Mon Sep 17 00:00:00 2001 From: Thomas Riccardi Date: Sun, 15 May 2016 17:40:50 +0200 Subject: [PATCH 043/718] disk/raid-mismatch-count better config: added .info per device --- plugins/disk/raid-mismatch-count | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/disk/raid-mismatch-count b/plugins/disk/raid-mismatch-count index bf4a89a7..e84c1dd1 100755 --- a/plugins/disk/raid-mismatch-count +++ b/plugins/disk/raid-mismatch-count @@ -51,6 +51,7 @@ __EOF__ for target in $targets; do echo "$target.label $target" echo "$target.critical 1" + echo "$target.info Mismatch count for software raid device $target" done exit fi From b805f7a64f537c11d4e3803a32e410997f7bccb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dr=2E=20Nagy=20Elem=C3=A9r=20K=C3=A1roly?= Date: Mon, 16 May 2016 22:11:31 +0000 Subject: [PATCH 044/718] Fixed typo in comment --- plugins/disk/raid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/disk/raid b/plugins/disk/raid index 120bc366..88f99dfe 100755 --- a/plugins/disk/raid +++ b/plugins/disk/raid @@ -60,7 +60,7 @@ while (@text) { $status = $3; } else { - # sencond line did not exist on /proc/mdstat + # second line did not exist on /proc/mdstat next; } From 807ba302f66ffe678dc87a51e275d031bd1de308 Mon Sep 17 00:00:00 2001 From: David Gilman Date: Sun, 22 Feb 2015 15:08:29 -0600 Subject: [PATCH 045/718] vnstat_month: suppress warnings in perl 5.18+ --- .travis.yml | 1 + plugins/network/vnstat_month | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 76144bd2..27d62d02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,6 +51,7 @@ install: - cpanm --notest XML::Smart - cpanm --notest XML::Twig - cpanm --notest nvidia::ml + - cpanm --notest experimental # - Sys::Virt version matching the test system's libvirt-dev - cpanm --notest DANBERR/Sys-Virt-0.9.8.tar.gz # Modules used by plugins, but missing on cpan diff --git a/plugins/network/vnstat_month b/plugins/network/vnstat_month index 8bd15620..6abc7ee2 100755 --- a/plugins/network/vnstat_month +++ b/plugins/network/vnstat_month @@ -4,6 +4,7 @@ use strict; use warnings; use autodie; use List::Util 'sum'; +use experimental 'switch'; =head1 NAME From 11e4124ca8e84c795ef1d79fb51043593f5a2ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Kn=C3=B6bel?= Date: Sat, 4 Jun 2016 14:48:42 +0200 Subject: [PATCH 046/718] add btrfs_subvol_usage plugin Plugin to monitor usage of subvolumes on a BTRFS filesystem. --- plugins/disk/btrfs_subvol_usage | 98 +++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 plugins/disk/btrfs_subvol_usage diff --git a/plugins/disk/btrfs_subvol_usage b/plugins/disk/btrfs_subvol_usage new file mode 100644 index 00000000..887eacc6 --- /dev/null +++ b/plugins/disk/btrfs_subvol_usage @@ -0,0 +1,98 @@ +#!/usr/bin/perl -w + +=head1 NAME + +btrfs_subvol_usage - Plugin to monitor usage of BTRFS subvolumes + +=head1 CONFIGURATION + +Must be run as root and you have to specify the path to the filesystem + +[btrfs_usage] +user root +env.fsroot /path/to/btrfs/filesystem + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 USAGE + +Link/Copy this plugin to /etc/munin/plugins/ and restart the munin-node. + +=head1 AUTHOR + +Alexander Knöbel + +=head1 LICENSE + +GPLv2 + +=cut + +use strict; +use Munin::Plugin; + +if ($ARGV[0] and $ARGV[0] eq 'autoconf') { + if (-e "/sbin/btrfs") { + print "yes\n"; + } else { + print "no (/sbin/btrfs is missing)\n"; + } + exit 0; +} + +my @fsroot; +if ( !defined( $ENV{"fsroot"} ) ) { + die "No fsroot given! See the manual at top of this plugin file."; +} +@fsroot = $ENV{"fsroot"}; + +# get subvolumes +my %subvols; +open(SVS, "btrfs subvolume list --sort=path @fsroot |") + or die("Failed to run 'btrfs subvolume list': " . $!); +while (my $line = ) { + chomp $line; + $line =~ s/ID ([0-9]+) gen ([0-9]+) top level ([0-9]+) path (.+)//; + $subvols{$1}->{id} = $1; + $subvols{$1}->{name} = $4; +} +close SVS; + +# get sizes from quota +open(QGS, "btrfs qgroup show @fsroot |") + or die("Failed to run 'btrfs qgroup show': " . $!); +while (my $line = ) { + chomp $line; + $line =~ s|([0-9]+)/([0-9]+)\s+([0-9]+)\s+([0-9]+)||; + if (defined($2) && defined($subvols{$2})) { + $subvols{$2}->{rfer} = $3; + $subvols{$2}->{excl} = $4; + } +} +close QGS; + +# print config +if ($ARGV[0] and $ARGV[0] eq 'config') { + print "btrfs_usage\n"; + print "graph_title BTRFS Subvolume usage\n"; + print "graph_args --base 1024 --lower-limit 0\n"; + print "graph_vlabel Bytes\n"; + print "graph_category disk\n"; + + for my $id (sort { $subvols{$a}->{name} cmp $subvols{$b}->{name} } keys %subvols) { + print "$id.label " . $subvols{$id}->{name} . "\n"; + print "$id.type GAUGE\n"; + print "$id.draw LINE2\n"; + } + + exit 0; +} + +# print usage +print "btrfs_subvol_usage\n"; +for my $id (sort { $subvols{$a}->{name} cmp $subvols{$b}->{name} } keys %subvols) { + print "$id.value " . $subvols{$id}->{rfer} . "\n"; +} From d44d1d1c5d0f01167bf01d7b3493be5d20d40171 Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Sat, 11 Jun 2016 04:03:56 +0200 Subject: [PATCH 047/718] Add basic support for RethinkDB one graph with read/write docs and overall requests --- plugins/rethinkdb/rethinkdb_node_io | 132 ++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 plugins/rethinkdb/rethinkdb_node_io diff --git a/plugins/rethinkdb/rethinkdb_node_io b/plugins/rethinkdb/rethinkdb_node_io new file mode 100644 index 00000000..bdae09e0 --- /dev/null +++ b/plugins/rethinkdb/rethinkdb_node_io @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +""" + rethinkdb_node_io - A munin plugin for Linux to monitor the io count + per second on the local node + + This plugin is licensed under the AGPL 3.0 license + + AGPL 3.0 RubenKelevra + Author: @RubenKelevra - + + This plugin is written with the known limitation to a single instance per + host. Patches which remove this limitation are very welcome. + + The following munin configuration parameters are supported: + #%# family=auto contrib + #%# capabilities=autoconf + +""" + +from importlib.util import find_spec +from multiprocessing import Process +from os import environ as env +from shutil import which +from socket import gethostname +from sys import argv +from sys import exit as fatal_ +from sys import stderr + +# functions +def fatal(status): + fatal_("ERROR: " + status) + + +def rclose_async(): + conn.close() + + +def check_load_rethinkdb() -> bool: + try: + rdb_spec = find_spec("rethinkdb") + if rdb_spec is None: + return False + return True + except: + fatal("Unknown error while try to load RethinkDB-Driver") + + +def eprint(*args, **kwargs): + print(*args, file=stderr, **kwargs) + + +def getFirstLine(respond): + assert isinstance(respond, net.DefaultCursor) + for e in respond: + return e + + +def print_config(): + print("graph_title RethinkDB - Local Database IOPS and Queries") + print("graph_args --base 1000 -l 0") + print("graph_vlabel Operations / second") + print("graph_category rethinkdb") + print("total_qps.label queries per sec") + print("total_rdps.label read docs per sec") + print("total_wdps.label written docs per sec") + exit(0) + + +def check_autoconf() -> bool: + # this might be too easy, but gonna try. + if which("rethinkdb"): + return True + return False + + +if __name__ == '__main__': + if len(argv) > 2: + fatal("unsupported argument count") + elif len(argv) == 2: + if str(argv[1]) == "config": + print_config() + elif str(argv[1]) == "autoconf": + if check_autoconf(): + print("yes") + else: + print("no") + if not check_load_rethinkdb(): + # FIXME: Correct display of error message when driver is missing should be checked + fatal("RethinkDB-Driver not available!") + exit(0) + else: + fatal("unsupported argument") + + if not check_load_rethinkdb(): + fatal("RethinkDB-Driver not available!") + from rethinkdb import net, connect, db + + # load environment + try: + RETHINKDB_PORT = env['rethinkdb_port'] + except: + RETHINKDB_PORT = "28015" + + try: + RETHINKDB_HOST = env['rethinkdb_host'] + except: + RETHINKDB_HOST = "localhost" + + try: + RETHINKDB_SERVERNAME = env['rethinkdb_servername'] + except: + RETHINKDB_SERVERNAME = gethostname() + + try: + conn = connect(RETHINKDB_HOST, RETHINKDB_PORT) + except: + fatal("connection attempt to the rethinkdb-host \"%s\" via port \"%s\" failed" % ( + str(RETHINKDB_HOST), str(RETHINKDB_PORT))) + + query_engine_info = getFirstLine( + db('rethinkdb').table("stats").filter({"server": RETHINKDB_SERVERNAME}).pluck('query_engine').limit(1).run( + conn))['query_engine'] + + rclose = Process(target=rclose_async()) + rclose.start() + + print("total_qps.value %s" % (query_engine_info["queries_total"])) + print("total_rdps.value %s" % (query_engine_info["read_docs_total"])) + print("total_wdps.value %s" % (query_engine_info["written_docs_total"])) + + # wait for connection termination + rclose.join() From 3720ba7aea4151e50bb104c7a66c2e1312f89a82 Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Sat, 11 Jun 2016 05:08:16 +0200 Subject: [PATCH 048/718] add counter-definition to store values fetched from database in the right way as diff --- plugins/rethinkdb/rethinkdb_node_io | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/rethinkdb/rethinkdb_node_io b/plugins/rethinkdb/rethinkdb_node_io index bdae09e0..010fdb94 100644 --- a/plugins/rethinkdb/rethinkdb_node_io +++ b/plugins/rethinkdb/rethinkdb_node_io @@ -60,9 +60,12 @@ def print_config(): print("graph_args --base 1000 -l 0") print("graph_vlabel Operations / second") print("graph_category rethinkdb") - print("total_qps.label queries per sec") - print("total_rdps.label read docs per sec") - print("total_wdps.label written docs per sec") + print("total_qps.label queries per + print("total_qps.type COUNTER") + print("total_rdps.label read docs + print("total_rdps.type COUNTER") + print("total_wdps.label written do + print("total_wdps.type COUNTER") exit(0) From c5c3bfdb0d48e0abab2501e5e4160e91e5d8fdf1 Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Sat, 11 Jun 2016 05:13:44 +0200 Subject: [PATCH 049/718] add some infos about env-vars to the headertext --- plugins/rethinkdb/rethinkdb_node_io | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/rethinkdb/rethinkdb_node_io b/plugins/rethinkdb/rethinkdb_node_io index 010fdb94..69f874c8 100644 --- a/plugins/rethinkdb/rethinkdb_node_io +++ b/plugins/rethinkdb/rethinkdb_node_io @@ -7,9 +7,18 @@ AGPL 3.0 RubenKelevra Author: @RubenKelevra - - + This plugin is written with the known limitation to a single instance per host. Patches which remove this limitation are very welcome. + + If your port / host is somewhat else than the default + localhost:28015, and/or your database-server differes in name from + `hostname` (short hostname), you can add rethinkdb-node-io config vars + like: + [rethinkdb_*] + env.rethinkdb_port 12345 + env.rethinkdb_host localhost.com + env.rethinkdb_servername localhost.com The following munin configuration parameters are supported: #%# family=auto contrib From 3fab7aca7aa7486d3599e0a3380668b5dfda5862 Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Sat, 11 Jun 2016 05:16:13 +0200 Subject: [PATCH 050/718] fix dubios format errors --- plugins/rethinkdb/rethinkdb_node_io | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/rethinkdb/rethinkdb_node_io b/plugins/rethinkdb/rethinkdb_node_io index 69f874c8..edda7fa1 100644 --- a/plugins/rethinkdb/rethinkdb_node_io +++ b/plugins/rethinkdb/rethinkdb_node_io @@ -69,11 +69,11 @@ def print_config(): print("graph_args --base 1000 -l 0") print("graph_vlabel Operations / second") print("graph_category rethinkdb") - print("total_qps.label queries per - print("total_qps.type COUNTER") - print("total_rdps.label read docs + print("total_qps.label queries per sec") + print("total_qps.type COUNTER") + print("total_rdps.label read docs per sec") print("total_rdps.type COUNTER") - print("total_wdps.label written do + print("total_wdps.label written docs per sec") print("total_wdps.type COUNTER") exit(0) From d1ca98563bdba70cc2736d25f5fe2387c1550e4e Mon Sep 17 00:00:00 2001 From: Nicolas Casar Gonzalez Date: Tue, 14 Jun 2016 15:23:56 -0300 Subject: [PATCH 051/718] Fix for pymongo > 3.0 and connection support of MongoClient only https://api.mongodb.com/python/current/changelog.html#mongoclient-changes --- plugins/mongodb/mongo_lag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mongodb/mongo_lag b/plugins/mongodb/mongo_lag index b70837fc..a54c0f50 100755 --- a/plugins/mongodb/mongo_lag +++ b/plugins/mongodb/mongo_lag @@ -25,7 +25,7 @@ import pymongo def _get_members(): host = os.environ.get('host', '127.0.0.1') port = os.environ.get('port', 27017) - conn = pymongo.Connection(host,port) + conn = pymongo.MongoClient(host,port) repl_status = conn.admin.command("replSetGetStatus") members = {} From 887063d53c434cdfd07f516ebcba88477899cae5 Mon Sep 17 00:00:00 2001 From: Nico Casar Gonzalez Date: Wed, 15 Jun 2016 00:28:38 -0300 Subject: [PATCH 052/718] Fix for pymongo > 3.0 and connection support of MongoClient https://api.mongodb.com/python/current/changelog.html#mongoclient-changes --- plugins/mongodb/mongo_collection_ | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/mongodb/mongo_collection_ b/plugins/mongodb/mongo_collection_ index baea31ae..a0b73653 100644 --- a/plugins/mongodb/mongo_collection_ +++ b/plugins/mongodb/mongo_collection_ @@ -1,4 +1,4 @@ -#!/usr/bin/env /usr/local/bin/python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: set sts=4 sw=4 encoding=utf-8 @@ -33,7 +33,7 @@ #%# capabilities=suggest autoconf -from pymongo import Connection +import pymongo from operator import itemgetter settings_host = '127.0.0.1' @@ -50,6 +50,7 @@ typeIndex['collcount']['title'] = 'per collection document count' typeIndex['collcount']['yaxis'] = 'documents' typeIndex['collcount']['base'] = '1000' typeIndex['collcount']['scale'] = '--logarithmic -l1' +typeIndex['collcount']['category'] = 'MongoDB' typeIndex['collsize'] = {} typeIndex['collsize']['index'] = 'size' @@ -57,6 +58,7 @@ typeIndex['collsize']['title'] = 'per collection data size' typeIndex['collsize']['yaxis'] = 'Byte' typeIndex['collsize']['base'] = '1024' typeIndex['collsize']['scale'] = '--logarithmic -l1 --units=si' +typeIndex['collsize']['category'] = 'MongoDB' typeIndex['avgsize'] = {} typeIndex['avgsize']['index'] = 'avgObjSize' @@ -64,6 +66,7 @@ typeIndex['avgsize']['title'] = 'average object size' typeIndex['avgsize']['yaxis'] = 'Byte' typeIndex['avgsize']['base'] = '1024' typeIndex['avgsize']['scale'] = '--logarithmic --units=si' +typeIndex['avgsize']['category'] = 'MongoDB' typeIndex['storage'] = {} typeIndex['storage']['index'] = 'storageSize' @@ -71,6 +74,7 @@ typeIndex['storage']['title'] = 'per collection storage size' typeIndex['storage']['yaxis'] = 'Byte' typeIndex['storage']['base'] = '1024' typeIndex['storage']['scale'] = '--logarithmic -l1 --units=si' +typeIndex['storage']['category'] = 'MongoDB' typeIndex['indexsize'] = {} typeIndex['indexsize']['index'] = 'totalIndexSize' @@ -78,11 +82,11 @@ typeIndex['indexsize']['title'] = 'per collection index size' typeIndex['indexsize']['yaxis'] = 'Byte' typeIndex['indexsize']['base'] = '1024' typeIndex['indexsize']['scale'] = '--logarithmic -l 1 --units=si' - +typeIndex['indexsize']['category'] = 'MongoDB' def getCollstats(graphtype): - con = Connection(settings_host, int(settings_port), slave_okay=True) + con = pymongo.MongoClient(settings_host, int(settings_port)) if settings_user: db = con['admin'] @@ -106,7 +110,7 @@ def getCollstats(graphtype): stats_tmp[collname]['value'] += long(stats[typeIndex[graphtype]['index']]) - con.disconnect() + con.close() for collname, item in sorted(stats_tmp.items()): yield ("%s" % collname, item['value'], item['dbname']) From 8837f68261fce0195247356969a408e646672d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20B=C3=B6sch-Plepelits?= Date: Fri, 17 Jun 2016 10:43:28 +0200 Subject: [PATCH 053/718] backuppc: Split age graph into last backup / last full backup --- plugins/backuppc/backuppc | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/plugins/backuppc/backuppc b/plugins/backuppc/backuppc index 222771e2..7d80cb93 100755 --- a/plugins/backuppc/backuppc +++ b/plugins/backuppc/backuppc @@ -44,22 +44,32 @@ if [ "$1" = "config" ]; then for h in ${HOSTS} 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 + echo "$(clean_fieldname ${h})_incr.label $(clean_fieldname ${h})" 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 + echo "multigraph backuppc_ages_full" + echo "graph_title BackupPC - Last Full Backup Age" + echo "graph_args -l 0" + echo "graph_vlabel days" + echo "graph_category Backuppc" + + for h in ${HOSTS} + do + echo "$(clean_fieldname ${h})_full.label $(clean_fieldname ${h})" + if [ -n "$full_warning" ]; then + echo "$(clean_fieldname ${h})_full.warning $full_warning" + fi + if [ -n "$full_critical" ]; then + echo "$(clean_fieldname ${h})_full.critical $full_critical" + fi + done + exit 0 fi @@ -74,11 +84,16 @@ done echo "multigraph backuppc_ages" for h in $HOSTS +do + SIZE=$(awk '{ age = systime() - $3 } END { print age / 3600 / 24; }' ${PCDIR}/${h}/backups) + echo "$(clean_fieldname ${h})_incr.value $SIZE" +done + +echo "multigraph backuppc_ages_full" +for h in $HOSTS do SIZE=$(awk '/full/ { age = systime() - $3 } END { print age / 3600 / 24; }' ${PCDIR}/${h}/backups) echo "$(clean_fieldname ${h})_full.value $SIZE" - SIZE=$(awk '/incr/ { age = systime() - $3 } END { print age / 3600 / 24; }' ${PCDIR}/${h}/backups) - echo "$(clean_fieldname ${h})_incr.value $SIZE" done <<'__END__' From 05d8c7c8089f17b2b7e7e032dd2f198c8f4308a4 Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Wed, 22 Jun 2016 01:23:34 +0200 Subject: [PATCH 054/718] add a servername to the graph to support multiple graphs per munin-node --- plugins/rethinkdb/rethinkdb_node_io | 31 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/plugins/rethinkdb/rethinkdb_node_io b/plugins/rethinkdb/rethinkdb_node_io index edda7fa1..ea7f9f40 100644 --- a/plugins/rethinkdb/rethinkdb_node_io +++ b/plugins/rethinkdb/rethinkdb_node_io @@ -1,18 +1,18 @@ #!/usr/bin/env python3 """ - rethinkdb_node_io - A munin plugin for Linux to monitor the io count + rethinkdb_node_io - A munin plugin for Linux to monitor the io count per second on the local node This plugin is licensed under the AGPL 3.0 license AGPL 3.0 RubenKelevra Author: @RubenKelevra - - + This plugin is written with the known limitation to a single instance per host. Patches which remove this limitation are very welcome. - - If your port / host is somewhat else than the default - localhost:28015, and/or your database-server differes in name from + + If your port / host is somewhat else than the default + localhost:28015, and/or your database-server differes in name from `hostname` (short hostname), you can add rethinkdb-node-io config vars like: [rethinkdb_*] @@ -35,6 +35,7 @@ from sys import argv from sys import exit as fatal_ from sys import stderr + # functions def fatal(status): fatal_("ERROR: " + status) @@ -64,17 +65,17 @@ def getFirstLine(respond): return e -def print_config(): - print("graph_title RethinkDB - Local Database IOPS and Queries") +def print_config(servername): + print("graph_title RethinkDB on '%s'- Local Database IOPS and Queries" % servername) print("graph_args --base 1000 -l 0") print("graph_vlabel Operations / second") print("graph_category rethinkdb") print("total_qps.label queries per sec") print("total_qps.type COUNTER") print("total_rdps.label read docs per sec") - print("total_rdps.type COUNTER") + print("total_rdps.type COUNTER") print("total_wdps.label written docs per sec") - print("total_wdps.type COUNTER") + print("total_wdps.type COUNTER") exit(0) @@ -86,11 +87,16 @@ def check_autoconf() -> bool: if __name__ == '__main__': + try: + RETHINKDB_SERVERNAME = env['rethinkdb_servername'] + except: + RETHINKDB_SERVERNAME = gethostname() + if len(argv) > 2: fatal("unsupported argument count") elif len(argv) == 2: if str(argv[1]) == "config": - print_config() + print_config(RETHINKDB_SERVERNAME) elif str(argv[1]) == "autoconf": if check_autoconf(): print("yes") @@ -118,11 +124,6 @@ if __name__ == '__main__': except: RETHINKDB_HOST = "localhost" - try: - RETHINKDB_SERVERNAME = env['rethinkdb_servername'] - except: - RETHINKDB_SERVERNAME = gethostname() - try: conn = connect(RETHINKDB_HOST, RETHINKDB_PORT) except: From 1e2bbe775e30b4f9fd1127124cc3cbbf71d33f90 Mon Sep 17 00:00:00 2001 From: Ken-ichi Mito Date: Mon, 18 May 2015 00:20:31 +0900 Subject: [PATCH 055/718] fix - add clean_fieldname to make field name safe * lvm_: only add clean_fieldname * lvm_snap_used * add clean_fieldname * force y-axis scale to 0-100 % --- plugins/disk/lvm_ | 5 ++++- plugins/disk/lvm_snap_used | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/plugins/disk/lvm_ b/plugins/disk/lvm_ index 6c2ba72f..dad888ec 100755 --- a/plugins/disk/lvm_ +++ b/plugins/disk/lvm_ @@ -44,6 +44,9 @@ munin-node. EOF +. $MUNIN_LIBDIR/plugins/plugin.sh + + if [ "$1" = "autoconf" ]; then if ! command -v lvs >/dev/null; then echo "no (lvs not found)" @@ -64,7 +67,7 @@ fi vg=`echo $0 | awk '{ sub(".*lvm_","",\$1); print \$1; }'` clean_name() { - echo $1 | sed 's/[\/.-]/_/g' + echo "$(clean_fieldname "$1")" } diff --git a/plugins/disk/lvm_snap_used b/plugins/disk/lvm_snap_used index 87574a24..91a34012 100755 --- a/plugins/disk/lvm_snap_used +++ b/plugins/disk/lvm_snap_used @@ -18,6 +18,9 @@ # 2012/01/27 - Sébastien Gross # - Fix lvdisplay path + +. $MUNIN_LIBDIR/plugins/plugin.sh + lvdisplay=$(which lvdisplay) if [ "$1" = "autoconf" ]; then @@ -35,10 +38,20 @@ if [ "$1" = "config" ]; then echo 'graph_title Allocated space for snapshot' echo 'graph_vlabel %' echo 'graph_category disk' - echo 'graph_args --base 100' - ${lvdisplay} -C | awk '$3 ~ /^s/{print $1".label "$1" snapshot of "$5} ' - exit 0 + echo 'graph_args -l 0 -u 100 -r' fi -${lvdisplay} -C | awk '$3 ~ /^s/{print $1".value",int($6)} ' +${lvdisplay} -C | awk '$3 ~ /^s/{print}' | while read line; do + name="$(echo $line | awk '{print $1}')" + id="$(clean_fieldname "$name")" + origin="$(echo $line | awk '{print $5}')" + origin="$(clean_fieldname "$origin")" + percent="$(echo $line | awk '{print $6}')" + + if [ "$1" = "config" ]; then + echo "$id.label $name snapshot of $origin" + else + echo "$id.value $percent" + fi +done From 5b0aad0638c8702ba386fc1c25ff838868454e3d Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Tue, 12 Jul 2016 15:35:16 +1000 Subject: [PATCH 056/718] [plugins/upnpc_] Add upnpc-based router monitoring plugin Signed-off-by: Olivier Mehani --- plugins/upnpc/upnpc_ | 149 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100755 plugins/upnpc/upnpc_ diff --git a/plugins/upnpc/upnpc_ b/plugins/upnpc/upnpc_ new file mode 100755 index 00000000..d45a7ebf --- /dev/null +++ b/plugins/upnpc/upnpc_ @@ -0,0 +1,149 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 NAME + +upnpc_ - Plugin to monitor routers via UPnP + +=head1 APPLICABLE SYSTEMS + +Linux systems with upnpc installed. + +=head1 CONFIGURATION + +None needed. + +=head1 AUTHOR + +Olivier Mehani + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + +autoconf() { + which upnpc >/dev/null && upnpc -s >/dev/null 2>&1 && echo yes || echo "no (No upnpc or no UPnP router)" +} + +suggest () { + upnpc -s | sed -n " \ + s/.*uptime=.*/uptime/p; \ + s/.*MaxBitRate.*/bitrate/p; \ + s/.*Bytes.*/traffic/p; \ + s/.*Packets.*/pkts/p; \ + " +} + +config () { + case $1 in + "uptime") + cat << EOF +graph_title Uplink connection uptime +graph_args -l 0 +graph_category network +graph_scale no +graph_vlabel uptime in hours +uptime.label uptime +uptime.draw AREA +uptime.cdef uptime,3600,/ +EOF + ;; + "bitrate") + cat << EOF +graph_title Uplink bitrate +graph_args --base 1000 -l 0 +graph_category network +graph_vlabel bitrate down (-) / up (+) +down.label bps +down.warning 4194304: +down.critical 1048576: +up.label bps +up.warning 524288: +up.critical 131072: +down.graph no +up.negative down +EOF + ;; + "traffic") + cat << EOF +graph_title Uplink traffic +graph_args --base 1024 -l 0 +graph_category network +graph_vlabel bytes in (-) / out (+) per ${graph_period} +down.label Bps +down.type DERIVE +down.min 0 +up.label Bps +up.type DERIVE +up.min 0 +down.graph no +up.negative down +EOF + ;; + "pkts") + cat << EOF +graph_title Uplink packets +graph_args --base 1000 -l 0 +graph_category network +graph_vlabel packets in (-) / out (+) per ${graph_period} +down.label pps +down.type DERIVE +down.min 0 +up.label pps +up.type DERIVE +up.min 0 +down.graph no +up.negative down +EOF + ;; + "*") + echo "$0: unknown mode '$1'" >&2 + exit 1 + esac +} + +fetch () { + case $1 in + "uptime") + upnpc -s | sed -n "s/.*uptime=\([0-9]\+\)s.*/uptime.value \1/p" + ;; + "bitrate") + upnpc -s | sed -n "s/^MaxBitRateDown : \([0-9]\+\) bps.*MaxBitRateUp \([0-9]\+\) bps.*/down.value \1\nup.value \2/p" + ;; + "traffic") + upnpc -s | sed -n "s/^Bytes:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" + ;; + "pkts") + upnpc -s | sed -n "s/^Packets:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" + ;; + "*") + echo "$0: unknown mode '$1'" >&2 + exit 1 + esac +} + +mode=`echo $0 | sed 's/.*_//'` + +case $1 in + "autoconf") + autoconf + ;; + "suggest") + suggest + ;; + "config") + config $mode + ;; + *) + fetch $mode + ;; +esac From cb11df95a9c6fd34b7e8d67e8d20d442009be715 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Fri, 15 Jul 2016 22:23:24 +1000 Subject: [PATCH 057/718] [sickbeard_episodes] Report snatched episodes Signed-off-by: Olivier Mehani --- plugins/sickbeard/sickbeard_episodes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/sickbeard/sickbeard_episodes b/plugins/sickbeard/sickbeard_episodes index 2a6eb7d5..4ba5f6f7 100755 --- a/plugins/sickbeard/sickbeard_episodes +++ b/plugins/sickbeard/sickbeard_episodes @@ -47,6 +47,7 @@ graph_title Episodes graph_vlabel Episodes graph_category Sick-Beard down.label Downloaded +down.snatched Snatched total.label Total EOC ; @@ -60,6 +61,7 @@ my $json = JSON::Any->jsonToObj($req->content()); if ($json->{result} eq 'success') { print "down.value $json->{data}->{ep_downloaded}\n"; + print "snatched.value $json->{data}->{ep_snatched}\n"; print "total.value $json->{data}->{ep_total}\n"; exit 0; } else { From 16bba8cc6fb7091a73d627bbeec2a6e05090498c Mon Sep 17 00:00:00 2001 From: swuschke Date: Wed, 3 Aug 2016 00:18:45 +0200 Subject: [PATCH 058/718] fix colored border and highlights --- .../munstrap/static/css/style-munstrap.css | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/templates/munstrap/static/css/style-munstrap.css b/templates/munstrap/static/css/style-munstrap.css index 474ac399..d2f8498e 100644 --- a/templates/munstrap/static/css/style-munstrap.css +++ b/templates/munstrap/static/css/style-munstrap.css @@ -24,7 +24,7 @@ img#zoom_image{ } .link-domain { - font-size: 1.4em; + font-size: 1.4em; color: #660066; } .link-host { @@ -35,10 +35,34 @@ ul.groupview, ul.groupview ul { list-style-type: none; } .munin-icon { - background: url(../img/logo-munin.png) left top; + background: url(../img/logo-munin.png) left top; margin-top: -6px; - width: 35px; + width: 35px; height: 35px; display: block; float: left; } + +img { + border: 2px solid transparent; +} + +img.warn { + border: 2px solid #8a6d3b; +} + +img.crit { + border: 2px solid #a94442; +} + +img.unkn { + border: 2px solid #ffaa00; +} + +.text-critical { + color: #a94442 +} + +.text-critical:hover { + color: #843534 +} \ No newline at end of file From ca71d12f290f1169a225e70b4418423afcbfc5e4 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Wed, 3 Aug 2016 12:49:53 +0200 Subject: [PATCH 059/718] added lxd memory plugin the lxd deamon provides a REST interface which can be queried by pylxd to get container related information. It stacks all containers, so the total memory footprint of lxd is visible. This plugin depends on python3 pylxd --- plugins/lxd/lxd_mem | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100755 plugins/lxd/lxd_mem diff --git a/plugins/lxd/lxd_mem b/plugins/lxd/lxd_mem new file mode 100755 index 00000000..9895133f --- /dev/null +++ b/plugins/lxd/lxd_mem @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import sys +from pylxd import api + +c=api.API() + +if len(sys.argv) == 2: + if sys.argv[1]=="autoconf": + print("yes") + sys.exit(0) + elif 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'])) From 46983fdc99f530476fd5c9abdf64b3be52ae3a9a Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Wed, 3 Aug 2016 12:50:28 +0200 Subject: [PATCH 060/718] added lxd disk plugin the lxd deamon provides a REST interface which can be queried by pylxd to get container related information. It graphs the disk usage of all disks in all containers. This plugin depends on python3 pylxd --- plugins/lxd/lxd_disk | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 plugins/lxd/lxd_disk diff --git a/plugins/lxd/lxd_disk b/plugins/lxd/lxd_disk new file mode 100755 index 00000000..b0e2671c --- /dev/null +++ b/plugins/lxd/lxd_disk @@ -0,0 +1,26 @@ +#!/usr/bin/python3 + +import sys +from pylxd import api + +c=api.API() + +if len(sys.argv) == 2: + if sys.argv[1]=="autoconf": + print("yes") + sys.exit(0) + elif 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'])) From 365e9932007b282b30fd10135c25e78d39866c83 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Wed, 3 Aug 2016 17:31:15 +0200 Subject: [PATCH 061/718] corrected lxd_ autoconf with proper checks the autoconf now checks for availablity of the pylxd module and access to the lxd socket. this ensures that the module will work properly. On failure, helpful errors are displayed --- plugins/lxd/lxd_disk | 54 +++++++++++++++++++++++++++++++------------- plugins/lxd/lxd_mem | 52 ++++++++++++++++++++++++++++++------------ 2 files changed, 75 insertions(+), 31 deletions(-) diff --git a/plugins/lxd/lxd_disk b/plugins/lxd/lxd_disk index b0e2671c..9e7c04f5 100755 --- a/plugins/lxd/lxd_disk +++ b/plugins/lxd/lxd_disk @@ -1,25 +1,47 @@ #!/usr/bin/python3 import sys -from pylxd import api -c=api.API() +errors=[] -if len(sys.argv) == 2: - if sys.argv[1]=="autoconf": +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") - sys.exit(0) - elif 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) + 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']: diff --git a/plugins/lxd/lxd_mem b/plugins/lxd/lxd_mem index 9895133f..6c797836 100755 --- a/plugins/lxd/lxd_mem +++ b/plugins/lxd/lxd_mem @@ -1,24 +1,46 @@ #!/usr/bin/python3 import sys -from pylxd import api -c=api.API() +errors=[] -if len(sys.argv) == 2: - if sys.argv[1]=="autoconf": +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") - sys.exit(0) - elif 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) + 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'])) From 21dfe488c0a8c3bf906f8751bf6bf751a2c04d49 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Fri, 5 Aug 2016 13:13:28 +1000 Subject: [PATCH 062/718] [ssl_] Allow to specify ports other than HTTPS Signed-off-by: Olivier Mehani --- plugins/ssl/ssl_ | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) mode change 100644 => 100755 plugins/ssl/ssl_ diff --git a/plugins/ssl/ssl_ b/plugins/ssl/ssl_ old mode 100644 new mode 100755 index 7f02b99d..c41e7b6e --- a/plugins/ssl/ssl_ +++ b/plugins/ssl/ssl_ @@ -28,7 +28,12 @@ Copyright (C) 2013 Patrick Domack . $MUNIN_LIBDIR/plugins/plugin.sh -SITE=${0##*ssl_} +ARGS=${0##*ssl_} +SITE=${ARGS/:*/} +PORT=${ARGS##*:} +if [ "$PORT" = "$SITE" ]; then + PORT=443 +fi case $1 in config) @@ -46,7 +51,7 @@ case $1 in ;; esac -cert=$(echo "" | openssl s_client -CApath /etc/ssl/certs -servername "${SITE}" -connect "${SITE}:443" 2>/dev/null); +cert=$(echo "" | openssl s_client -CApath /etc/ssl/certs -servername "${SITE}" -connect "${SITE}:${PORT}" 2>/dev/null); if [[ "${cert}" = *"-----BEGIN CERTIFICATE-----"* ]]; then echo "${cert}" | openssl x509 -noout -enddate | awk -F= 'BEGIN { split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", month, " "); for (i=1; i<=12; i++) mdigit[month[i]] = i; } /notAfter/ { split($0,a,"="); split(a[2],b," "); split(b[3],time,":"); datetime=b[4] " " mdigit[b[1]] " " b[2] " " time[1] " " time[2] " " time[3]; days=(mktime(datetime)-systime())/86400; print "expire.value " days; }' From 43e67ac7470a8193228518d715e8d13b6c1f8fe4 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Fri, 5 Aug 2016 14:04:42 +1000 Subject: [PATCH 063/718] [ssl_] Can't use colon in plugin names Signed-off-by: Olivier Mehani --- plugins/ssl/ssl_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ssl/ssl_ b/plugins/ssl/ssl_ index c41e7b6e..a4967efe 100755 --- a/plugins/ssl/ssl_ +++ b/plugins/ssl/ssl_ @@ -29,8 +29,8 @@ Copyright (C) 2013 Patrick Domack . $MUNIN_LIBDIR/plugins/plugin.sh ARGS=${0##*ssl_} -SITE=${ARGS/:*/} -PORT=${ARGS##*:} +SITE=${ARGS/_*/} +PORT=${ARGS##*_} if [ "$PORT" = "$SITE" ]; then PORT=443 fi From f7cce858656697ad5e1932aa25e1b9d7baa4ebe1 Mon Sep 17 00:00:00 2001 From: Wouter Verhelst Date: Mon, 8 Aug 2016 13:54:11 +0200 Subject: [PATCH 064/718] Add job stats graph --- plugins/sge/sge_job_stats | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 plugins/sge/sge_job_stats diff --git a/plugins/sge/sge_job_stats b/plugins/sge/sge_job_stats new file mode 100755 index 00000000..075af312 --- /dev/null +++ b/plugins/sge/sge_job_stats @@ -0,0 +1,39 @@ +#!/bin/bash + +# Graphs the number of running and waiting jobs, as well as when they +# are submitted. +# +# Tested with Son of Gridengine. + +if [ "$1" = "config" ] +then + echo graph_title SGE gridengine jobs + echo graph_vlabel count + echo graph_category processes + echo running.label Running jobs + echo running.type GAUGE + echo running.draw AREA + echo waiting.label Queued jobs + echo waiting.type GAUGE + echo waiting.draw STACK + echo maxnum.label Submitted jobs + echo maxnum.type DERIVE + echo maxnum.draw LINE2 + echo maxnum.min 0 + + exit 0 +fi + +. /opt/sge/default/common/settings.sh + +qstat -u '*' | awk ' + BEGIN{maxnum = 0; running = 0; waiting = 0} + /^ /{ + maxnum = (maxnum > $1 ? maxnum : $1); + if ( $5 == "r" ) { + running += 1; + } else { + waiting += 1; + } + } + END { printf("running.value %d\nwaiting.value %d\nmaxnum.value %d\n", running, waiting, maxnum); }' From c7ad869541732de9548882bef84f16a373862681 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Mon, 8 Aug 2016 11:45:22 -0400 Subject: [PATCH 065/718] Basic SenderBase plugin. --- plugins/senderbase/senderbase | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 plugins/senderbase/senderbase diff --git a/plugins/senderbase/senderbase b/plugins/senderbase/senderbase new file mode 100755 index 00000000..c0079366 --- /dev/null +++ b/plugins/senderbase/senderbase @@ -0,0 +1,20 @@ +#!/bin/sh + +real_ip_address=${ip_address:-$(hostname -I | head -1)} + +if [ "$1" = "config" ]; then + cat < Date: Mon, 8 Aug 2016 12:51:37 -0400 Subject: [PATCH 066/718] Documentation for SenderBase plugin. --- plugins/senderbase/senderbase | 45 +++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/plugins/senderbase/senderbase b/plugins/senderbase/senderbase index c0079366..cbfdb9a7 100755 --- a/plugins/senderbase/senderbase +++ b/plugins/senderbase/senderbase @@ -1,12 +1,55 @@ #!/bin/sh +: << =cut + +=head1 NAME + +senderbase - Gives the current SenderBase reputation for a mail host. + +=head1 CONFIGURATION + +By default, the script will use the first IP address returned by C. +If that's not the right address to use, set the C environment +variable to the appropriate value. + + [senderbase] + env.ip_address 8.8.8.8 + +=head1 AUTHOR + +Phil! Gold + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + real_ip_address=${ip_address:-$(hostname -I | head -1)} +ip_reversed=$(echo $real_ip_address | sed -re 's/^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/\4.\3.\2.\1/') +query_host=${ip_reversed}.rf.senderbase.org + +if [ "$1" = "autoconf" ]; then + if which dig >/dev/null; then + value=$(dig +short $query_host TXT | sed 's/"//g' | head -1) + if [ -n "$value" ]; then + echo 'yes' + else + echo "no (No SenderBase reputation for IP address ${ip_address}.)" + fi + else + echo 'no (No "dig" executable found.)' + fi + exit 0 +fi if [ "$1" = "config" ]; then cat < Date: Mon, 8 Aug 2016 21:56:15 -0500 Subject: [PATCH 067/718] Replace digit regex. Sometimes a digit column is blank and thus fails to be grabbed by the old regex. Just grab all of them and index as such. --- plugins/network/modem-nvg510 | 39 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/plugins/network/modem-nvg510 b/plugins/network/modem-nvg510 index e4f5fc2e..5b3b1791 100755 --- a/plugins/network/modem-nvg510 +++ b/plugins/network/modem-nvg510 @@ -28,24 +28,24 @@ use HTTP::Tiny; use constant { - down_rate => 0, - up_rate => 1, - sn_down => 14, - sn_up => 15, - line_attn_down => 16, - line_attn_up => 17, - power_down => 18, - power_up => 19, - err_sec_down => 20, - err_sec_up => 21, - los_down => 22, - los_up => 23, - lof_down => 24, - lof_up => 25, - fec_down => 26, - fec_up => 27, - crc_down => 28, - crc_up => 29, + down_rate => 2, + up_rate => 3, + sn_down => 23, + sn_up => 24, + line_attn_down => 25, + line_attn_up => 26, + power_down => 27, + power_up => 28, + err_sec_down => 29, + err_sec_up => 30, + los_down => 31, + los_up => 32, + lof_down => 33, + lof_up => 34, + fec_down => 35, + fec_up => 36, + crc_down => 37, + crc_up => 38, }; @@ -161,8 +161,9 @@ lof_up.min 0 my $url = $ENV{url} || "http://192.168.1.254/cgi-bin/dslstatistics.ha"; my $html = HTTP::Tiny->new(timeout => 1 )->get($url); die "Couldn't fetch $url" unless $html->{success}; -my @stats = $html->{content} =~ m{\s*([\d\.]+)\s*}g; +my @stats = $html->{content} =~ m{(.*?)}sg; +chomp(@stats); print qq|multigraph nvg510_speed down_rate.value $stats[down_rate] From 6eeb457fc21bf3997dd017ae6105c13b037565bf Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Mon, 8 Aug 2016 21:59:01 -0500 Subject: [PATCH 068/718] Use longer timeout. --- plugins/network/modem-nvg510 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/network/modem-nvg510 b/plugins/network/modem-nvg510 index 5b3b1791..00f7ca78 100755 --- a/plugins/network/modem-nvg510 +++ b/plugins/network/modem-nvg510 @@ -51,7 +51,7 @@ use constant { if(defined $ARGV[0] and $ARGV[0] eq 'autoconf'){ my $url = $ENV{url} || "http://192.168.1.254/cgi-bin/dslstatistics.ha"; - my $html = HTTP::Tiny->new(timeout => 1 )->get($url); + my $html = HTTP::Tiny->new(timeout => 30 )->get($url); if($html->{success} && $html->{content} =~ m{Broadband Status}){ print "yes\n"; @@ -159,7 +159,7 @@ lof_up.min 0 ########################## MAIN ############################# my $url = $ENV{url} || "http://192.168.1.254/cgi-bin/dslstatistics.ha"; -my $html = HTTP::Tiny->new(timeout => 1 )->get($url); +my $html = HTTP::Tiny->new(timeout => 30 )->get($url); die "Couldn't fetch $url" unless $html->{success}; my @stats = $html->{content} =~ m{(.*?)}sg; From 9f6ea4e487cc85cd65674dc00113abc9f6f56f33 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Tue, 9 Aug 2016 15:26:47 -0400 Subject: [PATCH 069/718] SenderBase limits were wrong. --- plugins/senderbase/senderbase | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/senderbase/senderbase b/plugins/senderbase/senderbase index cbfdb9a7..d00f5d18 100755 --- a/plugins/senderbase/senderbase +++ b/plugins/senderbase/senderbase @@ -51,8 +51,8 @@ graph_title SenderBase Reputation for $real_ip_address graph_info Current reputation from senderbase.org. Ranges from -10 (very poor) through 0 (neutral) to 10 (very good). graph_args --lower-limit -10 --upper-limit 10 reputation.label Reputation -reputation.warning :0 -reputation.critical :-5 +reputation.warning 0: +reputation.critical -5: EOF exit 0 fi From 8b010aadbd4922cc81ef0086136767e26bd92c62 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Thu, 11 Aug 2016 11:56:57 +0200 Subject: [PATCH 070/718] fixed lxc_disk with stopped containers stopped containers have no disk space info and the plugin crashed. Now it only shows disk usage for running containers --- plugins/lxd/lxd_disk | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/lxd/lxd_disk b/plugins/lxd/lxd_disk index 9e7c04f5..16366557 100755 --- a/plugins/lxd/lxd_disk +++ b/plugins/lxd/lxd_disk @@ -38,11 +38,15 @@ if len(sys.argv) == 2 and sys.argv[1]=="config": 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") + info = c.container_info(name) + if info['disk']: + for disk in info['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'])) + info = c.container_info(name) + if info['disk']: + for disk in info['disk']: + print(name+"-"+disk+".value "+str(info['disk'][disk]['usage'])) From 160bd2f034dd4b3cd8a5c9f2deea476254adb067 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sun, 14 Aug 2016 22:19:37 +1000 Subject: [PATCH 071/718] Add systemmd plugin counting units in each state Signed-off-by: Olivier Mehani --- plugins/system/systemd | 109 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100755 plugins/system/systemd diff --git a/plugins/system/systemd b/plugins/system/systemd new file mode 100755 index 00000000..810c3b07 --- /dev/null +++ b/plugins/system/systemd @@ -0,0 +1,109 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 NAME + +systemd - Plugin to monitor systemd units state + +=head1 APPLICABLE SYSTEMS + +Linux systems with systemd installed. + +=head1 CONFIGURATION + +None needed. + +=head1 AUTHOR + +Olivier Mehani + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + +states="active \ + reloading \ + inactive \ + failed \ + activating \ + deactivating" +autoconf() { + which systemctl >/dev/null && \ + systemctl --state=failed --no-pager --no-legend >/dev/null 2>&1 && echo yes || echo "no (No systemctl or error running it)" +} + +suggest() { + echo "units" +} + +config () { + case $1 in + "units") + cat << EOF +graph_title Systemd units state +graph_args -l 0 +graph_category system +graph_scale no +graph_vlabel units +EOF +for state in $states; do + echo "$state.label $state" + echo "$state.draw AREASTACK" + if [ $state = failed ]; then + echo "$state.warning 1" + echo "$state.critical 10" + fi +done + ;; + "*") + echo "$0: unknown mode '$1'" >&2 + exit 1 + esac +} + +fetch () { + case $1 in + "units") + tmp=`mktemp -t munin-systemd.XXXXXX` + systemctl --no-pager --no-legend --all | awk '{print $1, $3}' > $tmp + for state in \ + $states ; do + echo -n "$state.value " + grep $state$ $tmp | wc -l + echo -n "$state.extinfo " + echo $(grep $state$ $tmp | cut -d " " -f 1) + done + rm $tmp + ;; + "*") + echo "$0: unknown mode '$1'" >&2 + exit 1 + esac +} + +# mode=`echo $0 | sed 's/.*_//'` +mode=units + +case $1 in + "autoconf") + autoconf + ;; + "suggest") + suggest + ;; + "config") + config $mode + ;; + *) + fetch $mode + ;; +esac From 9121b90ee0baff9c88739c68b01bf6c4ed1fa22e Mon Sep 17 00:00:00 2001 From: Nico Casar Gonzalez Date: Thu, 18 Aug 2016 19:27:04 -0300 Subject: [PATCH 072/718] added mongodb URI for configuration defining settings_mongodb_uri will override settings_host and settings_port --- plugins/mongodb/mongo_collection_ | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/mongodb/mongo_collection_ b/plugins/mongodb/mongo_collection_ index a0b73653..67024d19 100644 --- a/plugins/mongodb/mongo_collection_ +++ b/plugins/mongodb/mongo_collection_ @@ -38,6 +38,8 @@ from operator import itemgetter settings_host = '127.0.0.1' settings_port = 27017 +# mongodb_uri will override host and port +settings_mongodb_uri = '' settings_db = 'mydb' settings_user = '' settings_password = '' @@ -86,7 +88,10 @@ typeIndex['indexsize']['category'] = 'MongoDB' def getCollstats(graphtype): - con = pymongo.MongoClient(settings_host, int(settings_port)) + if settings_mongodb_uri: + con = pymongo.MongoClient(settings_mongodb_uri) + else: + con = pymongo.MongoClient(settings_host, int(settings_port)) if settings_user: db = con['admin'] From 2d95020da7ae0dccf5e3e1390bfb84b586422a1d Mon Sep 17 00:00:00 2001 From: Samuel Smith Date: Mon, 29 Aug 2016 12:23:25 -0500 Subject: [PATCH 073/718] NOAA decommissioned weather.noaa.gov Change weather.noaa.gov -> tgftp.nws.noaa.gov --- plugins/weather/temperature_ | 6 +++--- plugins/weather/temperatures | 6 +++--- plugins/weather/weather_press_ | 6 +++--- plugins/weather/weather_temp_ | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/weather/temperature_ b/plugins/weather/temperature_ index f7187f7c..e81e6740 100755 --- a/plugins/weather/temperature_ +++ b/plugins/weather/temperature_ @@ -2,7 +2,7 @@ # # Copyright (C) 2006 Lars Strand # -# Plugin to fetch temperature from weather.noaa.gov +# Plugin to fetch temperature from tgftp.nws.noaa.gov # # Parameters supported: # @@ -16,7 +16,7 @@ use strict; my $usehum = $ENV{humidity} || undef; # set to "yes" to enable humidity -my $wcode = $ENV{wcode} || "ENGM"; # Find areacode here http://weather.noaa.gov/ +my $wcode = $ENV{wcode} || "ENGM"; # Find areacode here http://tgftp.nws.noaa.gov/ my $unit = $ENV{unit} || "C"; # "C" = Celsius, "F" = Fahrenheit my $proxy = $ENV{proxy} || undef; # Example: "http://proxy.foo.bar:8080/" @@ -41,7 +41,7 @@ if ($0 =~ /^(?:|.*\/)temperature_([^_]+)$/) { } -my $datasource = "http://weather.noaa.gov/pub/data/observations/metar/decoded/$wcode.TXT"; +my $datasource = "http://tgftp.nws.noaa.gov/data/observations/metar/decoded/$wcode.TXT"; my $ua = LWP::UserAgent->new(timeout => 30); $ua->agent('Munin'); diff --git a/plugins/weather/temperatures b/plugins/weather/temperatures index 18808faa..52711c8b 100755 --- a/plugins/weather/temperatures +++ b/plugins/weather/temperatures @@ -2,7 +2,7 @@ # # Copyright (C) 2006 Lars Strand # -# Plugin to fetch temperature from weather.noaa.gov +# Plugin to fetch temperature from tgftp.nws.noaa.gov # # Parameters supported: # @@ -15,7 +15,7 @@ use strict; -# Find areacodes here http://weather.noaa.gov/ +# Find areacodes here http://tgftp.nws.noaa.gov/ my @wcode = undef; if (defined($ENV{wcode})) { @@ -43,7 +43,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "autoconf") { } } -my $datasource = "http://weather.noaa.gov/pub/data/observations/metar/decoded/"; +my $datasource = "http://tgftp.nws.noaa.gov/data/observations/metar/decoded/"; my $ua = LWP::UserAgent->new(timeout => 30); $ua->agent('Munin'); diff --git a/plugins/weather/weather_press_ b/plugins/weather/weather_press_ index 28c28ba3..5dc79358 100755 --- a/plugins/weather/weather_press_ +++ b/plugins/weather/weather_press_ @@ -1,11 +1,11 @@ #!/usr/bin/env python """ -munin US NOAA weather plugin (http://weather.noaa.gov) +munin US NOAA weather plugin (http://tgftp.nws.noaa.gov) Draws pressure in hPa. Copy/link file as 'weather_pressure_CODE', like: weather_pressure_LOWW for Austria, Vienna. -Get the code by going to http://weather.noaa.gov, selecting your +Get the code by going to http://tgftp.nws.noaa.gov, selecting your location, and copying the code from the address bar of your browser; should be something like CODE.html. @@ -16,7 +16,7 @@ import sys import urllib import re -url = 'http://weather.noaa.gov/pub/data/observations/metar/decoded/%s.TXT' +url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' re_hPa = re.compile('Pressure.*\((\d+) hPa\)') diff --git a/plugins/weather/weather_temp_ b/plugins/weather/weather_temp_ index 5cd74b55..01c57494 100755 --- a/plugins/weather/weather_temp_ +++ b/plugins/weather/weather_temp_ @@ -1,11 +1,11 @@ #!/usr/bin/env python """ -munin US NOAA weather plugin (http://weather.noaa.gov) +munin US NOAA weather plugin (http://tgftp.nws.noaa.gov) Draws temperature/dew point in C. Copy/link file as 'weather_temp_CODE', like: weather_temp_LOWW for Austria, Vienna. -Get the code by going to http://weather.noaa.gov, selecting your +Get the code by going to http://tgftp.nws.noaa.gov, selecting your location, and copying the code from the address bar of your browser; should be something like CODE.html. @@ -16,7 +16,7 @@ import sys import urllib import re -url = 'http://weather.noaa.gov/pub/data/observations/metar/decoded/%s.TXT' +url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' re_C = re.compile('Temperature:.*\((-?\d+\.?\d?) C\)') re_DewC = re.compile('Dew.*\((-?\d+\.?\d?) C\)') From 5f33833cb5eebf2d67e191635ca218401d36407c Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Wed, 31 Aug 2016 21:31:30 +1000 Subject: [PATCH 074/718] [backup-manager] Rudimentary backup-manager plugin Signed-off-by: Olivier Mehani --- plugins/backup/backup-manager | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 plugins/backup/backup-manager diff --git a/plugins/backup/backup-manager b/plugins/backup/backup-manager new file mode 100755 index 00000000..d2eabd00 --- /dev/null +++ b/plugins/backup/backup-manager @@ -0,0 +1,39 @@ +#!/bin/sh + +# Example config: +# [backup-manager] +# user root +# env.backup_dir /path/to/your/backups/ +# env.lifetime 2 +# env.archive_pattern *.tar.bz2 +# env.backup_number 4 + +# Configuration directives, edit before first use. +BACKUP_DIR=${backup_dir:-/data/backup} +ARCHIVE_PATTERN="${archive_pattern:-*.tar.bz2}" +# How old backups should be considered as non-yound anymore in [days]. +LIFETIME=${lifetime:-2} +# Critical states will be issued when the number of fresh backups archives is below `backup_number`, +# and warnings below `backup_number*lifetime` +CRIT=${backup_number:-1} +WARN=$((${CRIT}*${LIFETIME})) + +# The situation is critical if there are no young files, the backup is down. +case $1 in + config) + cat << EOF +graph_title Fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} +graph_vlabel number +graph_args -l 0 +graph_category system +freshcount.label number +freshcount.critical ${CRIT}: +freshcount.warning ${WARN}: +EOF + exit 0;; +esac + +printf "freshcount.value " +find $BACKUP_DIR -name "${ARCHIVE_PATTERN}" -a -mtime -$LIFETIME | wc -l +printf "freshcount.extinfo " +du -sh $BACKUP_DIR From d0a837056f77c45a7ce7aebbdcbba8d541edbdbd Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 1 Sep 2016 10:49:19 +1000 Subject: [PATCH 075/718] [debsecan] Better label wording Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index 6d1ffb4d..1dba81f7 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -34,27 +34,27 @@ graph_args -l 0 --base 1000 graph_vlabel number of CVE graph_category system graph_period second -graph_info This graph show the number of known vulnerabilities present on your system. Use debsecan to see detail. +graph_info This graph show the number of known vulnerabilities present on your system. Use debsecan to see details. high.label high high.type GAUGE high.max 50000 high.min 0 -high.info The number CVE marked high high priority +high.info The number of CVEs marked high priority medium.label medium medium.type GAUGE medium.max 50000 medium.min 0 -medium.info The number CVE marked medium high priority +medium.info The number of CVEs marked medium priority low.label low low.type GAUGE low.max 50000 low.min 0 -low.info The number CVE marked low high priority +low.info The number of CVEs marked low priority other.label other other.type GAUGE other.max 50000 other.min 0 -other.info The number CVE with unspecified priority +other.info The number of CVEs with unspecified priority EOF_ exit 0 fi From 87f5a74ec85240107432c189119d3756e21df4e6 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 1 Sep 2016 10:50:00 +1000 Subject: [PATCH 076/718] [debsecan] Use temp filename rather than PID-derived Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index 1dba81f7..b0b27e9d 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -59,11 +59,12 @@ EOF_ exit 0 fi -debsecan 2> /dev/null > /tmp/debsecan.munin.$$ -high=`grep -c 'high urgency' /tmp/debsecan.munin.$$` -medium=`grep -c 'medium urgency' /tmp/debsecan.munin.$$` -low=`grep -c 'low urgency)' /tmp/debsecan.munin.$$` -other=`grep -c -v -e 'low urgency' -e 'medium urgency' -e 'high urgency' /tmp/debsecan.munin.$$` +OUT=`mktemp -t debescan.XXXXXX` +debsecan 2> /dev/null > ${OUT} +high=`grep -c 'high urgency' ${OUT}` +medium=`grep -c 'medium urgency' ${OUT}` +low=`grep -c 'low urgency)' ${OUT}` +other=`grep -c -v -e 'low urgency' -e 'medium urgency' -e 'high urgency' ${OUT}` cat < Date: Thu, 1 Sep 2016 10:50:19 +1000 Subject: [PATCH 077/718] [debsecan] Use stacked areas, and colour-code urgency Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index b0b27e9d..4cbf40ba 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -36,23 +36,27 @@ graph_category system graph_period second graph_info This graph show the number of known vulnerabilities present on your system. Use debsecan to see details. high.label high +high.colour FF0000 high.type GAUGE -high.max 50000 +high.draw AREASTACK high.min 0 high.info The number of CVEs marked high priority medium.label medium +medium.colour FFA500 medium.type GAUGE -medium.max 50000 +medium.draw AREASTACK medium.min 0 medium.info The number of CVEs marked medium priority low.label low +low.colour 0000FF low.type GAUGE -low.max 50000 +low.draw AREASTACK low.min 0 low.info The number of CVEs marked low priority other.label other +other.colour 00A5FF other.type GAUGE -other.max 50000 +other.draw AREASTACK other.min 0 other.info The number of CVEs with unspecified priority EOF_ From 4653dcd9a696faa8236fc4b9f61a16885049b12a Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 1 Sep 2016 11:17:47 +1000 Subject: [PATCH 078/718] [debescan] Add links to CVEs in extinfo Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index 4cbf40ba..047615eb 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -63,17 +63,24 @@ EOF_ exit 0 fi +CVERE="\(\(CVE\|TMP\)[-0-9A-Fa-f]\+\)" +CVEBASEURL="https://security-tracker.debian.org/tracker/" + OUT=`mktemp -t debescan.XXXXXX` debsecan 2> /dev/null > ${OUT} high=`grep -c 'high urgency' ${OUT}` medium=`grep -c 'medium urgency' ${OUT}` low=`grep -c 'low urgency)' ${OUT}` -other=`grep -c -v -e 'low urgency' -e 'medium urgency' -e 'high urgency' ${OUT}` +other=`grep -c -v '\(low\|medium\|high\) urgency' ${OUT}` cat <\1 #p" ${OUT})` medium.value $medium +medium.extinfo `echo $(sed -n "s#^${CVERE}.*medium urgency.*#\1 #p" ${OUT})` low.value $low +low.extinfo `echo $(sed -n "s#^${CVERE}.*low urgency.*#\1 #p" ${OUT})` other.value $other +other.extinfo `echo $(grep -v -e '\(low\|medium\|high\) urgency' ${OUT} | sed -n "s#^${CVERE}.*#\1 #p")` EOF_ rm -f ${OUT} From 719190a5424395fcdfb175b0ec6edf49382ab1e5 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 1 Sep 2016 11:19:27 +1000 Subject: [PATCH 079/718] [debescan] Can't add HMTL to extinfo ): Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index 047615eb..b8aec1eb 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -64,7 +64,6 @@ EOF_ fi CVERE="\(\(CVE\|TMP\)[-0-9A-Fa-f]\+\)" -CVEBASEURL="https://security-tracker.debian.org/tracker/" OUT=`mktemp -t debescan.XXXXXX` debsecan 2> /dev/null > ${OUT} @@ -74,13 +73,13 @@ low=`grep -c 'low urgency)' ${OUT}` other=`grep -c -v '\(low\|medium\|high\) urgency' ${OUT}` cat <\1 #p" ${OUT})` +high.extinfo `echo $(sed -n "s#^${CVERE}.*high urgency.*#\1 #p" ${OUT})` medium.value $medium -medium.extinfo `echo $(sed -n "s#^${CVERE}.*medium urgency.*#\1 #p" ${OUT})` +medium.extinfo `echo $(sed -n "s#^${CVERE}.*medium urgency.*#\1 #p" ${OUT})` low.value $low -low.extinfo `echo $(sed -n "s#^${CVERE}.*low urgency.*#\1 #p" ${OUT})` +low.extinfo `echo $(sed -n "s#^${CVERE}.*low urgency.*#\1 #p" ${OUT})` other.value $other -other.extinfo `echo $(grep -v -e '\(low\|medium\|high\) urgency' ${OUT} | sed -n "s#^${CVERE}.*#\1 #p")` -EOF_ +other.extinfo `echo $(grep -v -e '\(low\|medium\|high\) urgency' ${OUT} | sed -n "s#^${CVERE}.*#\1 #p")` +EOF rm -f ${OUT} From b80913c039ca1c437e45be855c5ea26a6a842258 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 1 Sep 2016 11:23:47 +1000 Subject: [PATCH 080/718] [debescan] Remove stray whitespaces Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index b8aec1eb..cf4eb4aa 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -13,7 +13,7 @@ #%# capabilities=autoconf # Auto enable if we have debsecan only -if [ "$1" = "autoconf" ] ; then +if [ "$1" = "autoconf" ] ; then if [ -x /usr/bin/debsecan ]; then echo yes else @@ -22,7 +22,7 @@ if [ "$1" = "autoconf" ] ; then exit 0 fi -# Fail if we don't have debsecan +# Fail if we don't have debsecan if [ ! -x /usr/bin/debsecan ]; then exit 1 fi From 7487332a751ebe0cba1c24279f97043285a1630a Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 1 Sep 2016 20:33:10 +1000 Subject: [PATCH 081/718] [debsecan] Show package with CVE counts rather than CVEs Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index cf4eb4aa..c2103f38 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -64,22 +64,33 @@ EOF_ fi CVERE="\(\(CVE\|TMP\)[-0-9A-Fa-f]\+\)" +CVECOUNTRE="s/^.*\([0-9]\+\) \+\([^ ]\+\)/\2 (\1)/" OUT=`mktemp -t debescan.XXXXXX` +HIGH=`mktemp -t debescan.XXXXXX` +MEDIUM=`mktemp -t debescan.XXXXXX` +LOW=`mktemp -t debescan.XXXXXX` +OTHER=`mktemp -t debescan.XXXXXX` debsecan 2> /dev/null > ${OUT} -high=`grep -c 'high urgency' ${OUT}` -medium=`grep -c 'medium urgency' ${OUT}` -low=`grep -c 'low urgency)' ${OUT}` -other=`grep -c -v '\(low\|medium\|high\) urgency' ${OUT}` -cat < ${HIGH} +grep 'medium urgency' ${OUT} > ${MEDIUM} +grep 'low urgency)' ${OUT} > ${LOW} +grep -v '\(low\|medium\|high\) urgency' ${OUT} > ${OTHER} + +high=`cat ${HIGH} | wc -l` +medium=`cat ${MEDIUM} | wc -l` +low=`cat ${LOW} | wc -l` +other=`cat ${OTHER} | wc -l` + +cat < Date: Fri, 2 Sep 2016 10:05:08 +1000 Subject: [PATCH 082/718] [debsecan] Typo in temp filename Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index c2103f38..a4f7940f 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -66,11 +66,11 @@ fi CVERE="\(\(CVE\|TMP\)[-0-9A-Fa-f]\+\)" CVECOUNTRE="s/^.*\([0-9]\+\) \+\([^ ]\+\)/\2 (\1)/" -OUT=`mktemp -t debescan.XXXXXX` -HIGH=`mktemp -t debescan.XXXXXX` -MEDIUM=`mktemp -t debescan.XXXXXX` -LOW=`mktemp -t debescan.XXXXXX` -OTHER=`mktemp -t debescan.XXXXXX` +OUT=`mktemp -t debsecan.XXXXXX` +HIGH=`mktemp -t debsecan.XXXXXX` +MEDIUM=`mktemp -t debsecan.XXXXXX` +LOW=`mktemp -t debsecan.XXXXXX` +OTHER=`mktemp -t debsecan.XXXXXX` debsecan 2> /dev/null > ${OUT} grep 'high urgency' ${OUT} > ${HIGH} grep 'medium urgency' ${OUT} > ${MEDIUM} From 4a206ac9fde652787ba0fcafb15e54e9481aabab Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sat, 3 Sep 2016 14:09:37 +1000 Subject: [PATCH 083/718] [deborphan] New plugin counting orphaned packages in all sections Signed-off-by: Olivier Mehani --- plugins/system/deborphan | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100755 plugins/system/deborphan diff --git a/plugins/system/deborphan b/plugins/system/deborphan new file mode 100755 index 00000000..19eafee2 --- /dev/null +++ b/plugins/system/deborphan @@ -0,0 +1,64 @@ +#!/bin/bash +# +# Plugin to monitor the number of orphaned packages on the +# system (using deborphan). Might work on section distib, who knows... +# +# Based on the debsecan plugin by Nicolas Bouthors. 2016-09-02 +# +# Olivie Mehani +# +# Licence : GPLv2 +# +#%# family=auto +#%# capabilities=autoconf + +# Auto enable if we have deborphan only +if [ "$1" = "autoconf" ] ; then + if [ -x /usr/bin/deborphan ]; then + echo yes + else + echo no + fi + exit 0 +fi + +# Fail if we don't have deborphan +if [ ! -x /usr/bin/deborphan ]; then + exit 1 +fi + +OUT=`mktemp -t deborphan.XXXXXX` +deborphan --show-section --guess-all | sed 's/\//_/' | sort > ${OUT} + +CATEGORIES="$(sed 's/ .*//' ${OUT} | uniq)" + +if [ "$1" = "config" ]; then + cat < ${TMP} + echo "${cat}.value `cat ${TMP} | wc -l`" + echo "${cat}.extinfo `echo $(cat ${TMP})`" + rm ${TMP} + done +fi + +rm -f ${OUT} From c3d5e109c00ba949a493f8b628e7cbc04a20f35b Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sat, 3 Sep 2016 21:04:25 +1000 Subject: [PATCH 084/718] [file_length_] A versatile plugin to count the lines in specified files Signed-off-by: Olivier Mehani --- plugins/system/file_length_ | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100755 plugins/system/file_length_ diff --git a/plugins/system/file_length_ b/plugins/system/file_length_ new file mode 100755 index 00000000..7aff3e5e --- /dev/null +++ b/plugins/system/file_length_ @@ -0,0 +1,63 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + +file_length_ - Plugin to monitor the length of specified files + +Useful for things such as lists (white, black, user, ...). + +=head1 CONFIGURATION + + [file_length_IDENTIFIER] + env.files FILEPATHGLOB1 FILEPATHGLOB2 ... + env.category DEFAULTS_TO_system + env.title OPTIONAL_TITLE + +=head1 AUTHOR + +Olivier Mehani (based on disk/log_sizes) + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + +#NAME=`echo $0 | sed 's/.*_//'` +NAME=${0#*_} +TITLE=${title:-File lengths for $NAME} +CATEGORY=${category:-system} + +FILES=${files:-/var/log/messages} +FILES=$(echo $(ls $FILES)) + +if [ "$1" = "config" ] ; then + cat < Date: Sun, 4 Sep 2016 14:15:20 +1000 Subject: [PATCH 085/718] [file_length_] Add logarithmic option Signed-off-by: Olivier Mehani --- plugins/system/file_length_ | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/system/file_length_ b/plugins/system/file_length_ index 7aff3e5e..27a30c10 100755 --- a/plugins/system/file_length_ +++ b/plugins/system/file_length_ @@ -14,6 +14,7 @@ Useful for things such as lists (white, black, user, ...). env.files FILEPATHGLOB1 FILEPATHGLOB2 ... env.category DEFAULTS_TO_system env.title OPTIONAL_TITLE + env.logarithmic 1 =head1 AUTHOR @@ -38,13 +39,19 @@ CATEGORY=${category:-system} FILES=${files:-/var/log/messages} FILES=$(echo $(ls $FILES)) + if [ "$1" = "config" ] ; then + if [ ${logarithmic} = 1 ]; then + graph_args="-o" + else + graph_args="-l 0" + fi cat < Date: Wed, 7 Sep 2016 14:58:52 +1000 Subject: [PATCH 086/718] [debsecan] Report fixed vulnerabilities, add config and doc Signed-off-by: Olivier Mehani --- plugins/system/debsecan | 74 +++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/plugins/system/debsecan b/plugins/system/debsecan index a4f7940f..5f63e679 100755 --- a/plugins/system/debsecan +++ b/plugins/system/debsecan @@ -1,16 +1,34 @@ #!/bin/sh -# -# Plugin to monitor the number of CVE vulnerabilities present on a Debian -# system (using debsecan). Might work on other distib, who knows... -# -# Inspiration of the moment 10/10/2007 -# -# Nicolas BOUTHORS http://nbi.fr/ -# -# Licence : Public Domain -# -#%# family=auto -#%# capabilities=autoconf + +: << =cut + +=head1 NAME + +debsecan - Plugin to monitor the number of CVE vulnerabilities present on a Debian +system (using debsecan). Might work on other distib, who knows... + +=head1 CONFIGURATION + + [debsecan] + env.suite jessie + env.fixed_warn 1 + env.fixed_critical 1000 + +=head1 AUTHORS + +* Nicolas BOUTHORS http://nbi.fr/, Inspiration of the moment 10/10/2007 +* Olivier Mehani , 2016 + +=head1 LICENSE + +Public Domain + +=head1 MAGIC MARKERS + +%# family=auto +%# capabilities=autoconf + +=cut # Auto enable if we have debsecan only if [ "$1" = "autoconf" ] ; then @@ -27,9 +45,19 @@ if [ ! -x /usr/bin/debsecan ]; then exit 1 fi +# Determine suite from filename... +SUITE=`echo $0 | sed 's/.*_//'` +if [ ${SUITE} = ${0} ]; then + # ...or fall back onto configuration in environment + SUITE=${suite:-sid} +fi +FIXEDWARN=${fixed_warning:-1} +FIXEDCRIT=${fixed_critical:-1000} + +CVERE="\(\(CVE\|TMP\)[-0-9A-Fa-f]\+\)" if [ "$1" = "config" ] ; then cat < /dev/null > ${OUT} +FIXED=`mktemp -t debsecan.XXXXXX` +debsecan --suite ${SUITE} 2> /dev/null > ${OUT} grep 'high urgency' ${OUT} > ${HIGH} grep 'medium urgency' ${OUT} > ${MEDIUM} grep 'low urgency)' ${OUT} > ${LOW} -grep -v '\(low\|medium\|high\) urgency' ${OUT} > ${OTHER} +grep '(fixed' ${OUT} > ${FIXED} high=`cat ${HIGH} | wc -l` medium=`cat ${MEDIUM} | wc -l` low=`cat ${LOW} | wc -l` other=`cat ${OTHER} | wc -l` +fixed=`cat ${FIXED} | wc -l` cat < Date: Wed, 7 Sep 2016 14:57:49 +1000 Subject: [PATCH 087/718] [file_length_] Remove incorrect markers Signed-off-by: Olivier Mehani --- plugins/system/file_length_ | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/system/file_length_ b/plugins/system/file_length_ index 27a30c10..a0e94480 100755 --- a/plugins/system/file_length_ +++ b/plugins/system/file_length_ @@ -24,11 +24,6 @@ Olivier Mehani (based on disk/log_sizes) GPLv2 -=head1 MAGIC MARKERS - - #%# family=auto - #%# capabilities=autoconf suggest - =cut #NAME=`echo $0 | sed 's/.*_//'` From 0057c6607e0d18fa0ca8089e6759297a97b1ca25 Mon Sep 17 00:00:00 2001 From: Guillaume Marsay Date: Mon, 26 Sep 2016 10:38:07 +0200 Subject: [PATCH 088/718] Add Chilli plugin for sessions state --- plugins/chilli/chilli_ | 118 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100755 plugins/chilli/chilli_ diff --git a/plugins/chilli/chilli_ b/plugins/chilli/chilli_ new file mode 100755 index 00000000..e5df6999 --- /dev/null +++ b/plugins/chilli/chilli_ @@ -0,0 +1,118 @@ +#!/bin/bash +# -*- sh -*- + +: << =cut + +=head1 NAME + +chilli_ - Wildcard-plugin to monitor sessions state on Coova Chilli. + +=head1 CONFIGURATION + +This plugin does not normally require configuration. + +The plugin may need to run as root. This is configured like this: + + [chilli_*] + user root + +This is a wildcard plugin. To monitor an instance, link +chilli_ to this file. For example : + + ln -s /usr/share/munin/plugins/chilli_ \ + /etc/munin/plugins/chilli_hotspot1 + +will monitor hotspot1. + +For monitor all instances use : + + ln -s /usr/share/munin/plugins/chilli_ \ + /etc/munin/plugins/chilli_total + +=head1 AUTHOR + +OPENevents - Guillaume Marsay + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + + +INSTANCE=${0##*chilli_} +CHILLI_PATH_BIN="/usr/sbin/chilli_query" +CHILLI_PATH_SOCK="/var/run" + + +case $1 in + autoconf) + if [[ -r $CHILLI_PATH_BIN ]]; then + if [[ $INSTANCE == "total" ]]; then + echo "yes" + exit 0 + else + if [[ -r $CHILLI_PATH_SOCK/chilli_$INSTANCE.sock ]]; then + echo yes + exit 0 + else + echo "no ($CHILLI_PATH_SOCK/chilli_$INSTANCE.sock not found)" + exit 0 + fi + fi + else + echo "no ($CHILLI_PATH_BIN not found)" + exit 0 + fi + ;; + suggest) + INSTANCES_LIST=$(ls /var/run/chilli_*.sock) + + for file in $INSTANCES_LIST; do + echo $(basename $file .sock | cut -d _ -f 2) + done + + echo "total" + + exit 0 + ;; + config) + echo "graph_title Chilli $INSTANCE sessions" + echo "graph_args --base 1000 -l 0" + echo "graph_category chilli" + echo "none.label NONE" + echo "none.min 0" + echo "none.draw AREA" + echo "none.colour ff8000" + echo "dnat.label DNAT" + echo "dnat.min 0" + echo "dnat.draw STACK" + echo "dnat.colour 0066b3" + echo "pass.label PASS" + echo "pass.draw STACK" + echo "pass.min 0" + echo "pass.colour 00cc00" + + exit 0 + ;; +esac + + +if [[ $INSTANCE == "total" ]]; then + STATE_PASS=$($CHILLI_PATH_BIN list | grep "pass" | wc -l) + STATE_DNAT=$($CHILLI_PATH_BIN list | grep "dnat" | wc -l) + STATE_NONE=$($CHILLI_PATH_BIN list | grep "none" | wc -l) +else + STATE_PASS=$($CHILLI_PATH_BIN -s $CHILLI_PATH_SOCK/chilli_$INSTANCE.sock list | grep "pass" | wc -l) + STATE_DNAT=$($CHILLI_PATH_BIN -s $CHILLI_PATH_SOCK/chilli_$INSTANCE.sock list | grep "dnat" | wc -l) + STATE_NONE=$($CHILLI_PATH_BIN -s $CHILLI_PATH_SOCK/chilli_$INSTANCE.sock list | grep "none" | wc -l) +fi + +echo "pass.value $STATE_PASS" +echo "dnat.value $STATE_DNAT" +echo "none.value $STATE_NONE" From a582c4bae29c34dca07b06256e26ea178a444699 Mon Sep 17 00:00:00 2001 From: Tomaz Solc Date: Sat, 1 Oct 2016 09:23:38 +0200 Subject: [PATCH 089/718] dhcp-pool: Ignore comments in conffile. This prevents dhcp-pool from detecting commented-out "range" statements. --- plugins/network/dhcp-pool | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/network/dhcp-pool b/plugins/network/dhcp-pool index 2cb715dd..23ab9fc0 100755 --- a/plugins/network/dhcp-pool +++ b/plugins/network/dhcp-pool @@ -98,6 +98,8 @@ sub determine_pools { close (CONFFILE); foreach $line (@conffile) { + next if $line =~ /^\s*#/; + if ($line =~ /range[\s]+([\d]+\.[\d]+\.[\d]+\.[\d]+)[\s]+([\d]+\.[\d]+\.[\d]+\.[\d]+)/) { $start = string2ip($1); $end = string2ip($2); From 316a59e156dde4e6bb76d064e08864fbec39dfdc Mon Sep 17 00:00:00 2001 From: Tomaz Solc Date: Sat, 1 Oct 2016 09:25:51 +0200 Subject: [PATCH 090/718] dhcp-pool: Fix off-by-one error in pool size. "range" statement gives the lowest and highest IP addresses (both inclusive) in a pool. dhcp-pool did not count the highest address when calculating pool size. --- plugins/network/dhcp-pool | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/network/dhcp-pool b/plugins/network/dhcp-pool index 23ab9fc0..14874d99 100755 --- a/plugins/network/dhcp-pool +++ b/plugins/network/dhcp-pool @@ -73,7 +73,7 @@ else { # For each pool, count how many leases from that pool are currently active foreach $start (keys %pools) { $size = $pools{$start}; - $end = $start+$size; + $end = $start+$size-1; $free = $size; foreach $lease (@activeleases) { @@ -103,9 +103,12 @@ sub determine_pools { if ($line =~ /range[\s]+([\d]+\.[\d]+\.[\d]+\.[\d]+)[\s]+([\d]+\.[\d]+\.[\d]+\.[\d]+)/) { $start = string2ip($1); $end = string2ip($2); - $size = $end - $start; + defined($start) || next; defined($end) || next; + + # The range statement gives the lowest and highest IP addresses in a range. + $size = $end - $start + 1; $pools{$start} = $size; } From 7f2ae83eb6b1c9f457b9c652101a01542b66693e Mon Sep 17 00:00:00 2001 From: Tomaz Solc Date: Mon, 3 Oct 2016 18:29:40 +0200 Subject: [PATCH 091/718] systemd: don't print out empty extinfo lines This fixes warnings like this in munin-update.log: [WARNING] 4 lines had errors while 8 lines were correct (33. 33%) in data from 'fetch systemd' --- plugins/system/systemd | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/system/systemd b/plugins/system/systemd index 810c3b07..1f06fa8d 100755 --- a/plugins/system/systemd +++ b/plugins/system/systemd @@ -79,8 +79,10 @@ fetch () { $states ; do echo -n "$state.value " grep $state$ $tmp | wc -l - echo -n "$state.extinfo " - echo $(grep $state$ $tmp | cut -d " " -f 1) + extinfo=`grep $state$ $tmp | cut -d " " -f 1` + if [ "$extinfo" ]; then + echo "$state.extinfo" $extinfo + fi done rm $tmp ;; From 1568bf67ea34b1ae59d3846a66f106ebce5702a4 Mon Sep 17 00:00:00 2001 From: darac Date: Tue, 4 Oct 2016 11:03:24 +0100 Subject: [PATCH 092/718] Update boinc_projs --- plugins/boinc/boinc_projs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/boinc/boinc_projs b/plugins/boinc/boinc_projs index 37d1d713..8769b3be 100755 --- a/plugins/boinc/boinc_projs +++ b/plugins/boinc/boinc_projs @@ -24,6 +24,10 @@ # # $Log$ # +# Revision 1.2 2016/10/04 Paul Saunders +# BoincCmd now translates states into words, so consider a "downloaded, scheduled, EXECUTING" task +# to be equivalent to a "2, 2, 1" task +# Really, this should be ported to use the proper RPC, rather than parsing boinccmd's output. # Revision 1.1 2011/03/22 Paul Saunders # Update for BOINC 6.12 # Add colours from http://boinc.netsoft-online.com/e107_plugins/forum/forum_viewtopic.php?3 @@ -145,7 +149,8 @@ for my $rslt_info (@rsltInfos) { my @acttask = grep /^\s+active_task_state: /,@lines; my $acttask = $acttask[0]; $acttask =~ s/^\s+active_task_state: //; - if ( ($schedstat eq "2") && ($state eq "2") && ($acttask eq "1") ) { + if (( ($schedstat eq "2") && ($state eq "2") && ($acttask eq "1") ) || + ( ($schedstate eq "scheduled") && ($state eq "downloaded") && ($acttask eq "EXECUTING") )) { # This is running task $projects{$url}->{prj_running} += 1; } From 56cd2c926ca99e396a0b6c2ebee76394bb4de195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sun, 16 Oct 2016 23:03:16 +0000 Subject: [PATCH 093/718] Make munin_events POSIX compatible --- plugins/munin/munin_events | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/munin/munin_events b/plugins/munin/munin_events index 121f1695..26498de3 100755 --- a/plugins/munin/munin_events +++ b/plugins/munin/munin_events @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # -*- sh -*- : <<=cut @@ -39,7 +39,7 @@ INFO, WARNING, ERROR, FATAL. =head1 VERSION - 1.2.20160514 + 1.2.20161017 =head1 AUTHOR @@ -55,7 +55,8 @@ GPLv2 ############################## # Includes -. $MUNIN_LIBDIR/plugins/plugin.sh +# shellcheck disable=SC1090 +. "$MUNIN_LIBDIR/plugins/plugin.sh" ############################## # Configurable variables @@ -67,12 +68,11 @@ logtail_bin=${logtail_bin:-/usr/sbin/logtail2} # Print one value do_value() { - local FIELD="$1" - local EVENT_LABEL="$2" - local EVENT_COUNT - + FIELD="$1" + EVENT_LABEL="$2" EVENT_COUNT="$("$logtail_bin" -t "$muninupdate" 2> /dev/null | grep -c "^[0-9/: ]\{19\} \[${EVENT_LABEL}\]")" - if ! [ -z "${EVENT_COUNT//[0-9]/}" ]; then + + if ! [ -z "$(echo "$EVENT_COUNT" | sed 's|[0-9]\+||')" ]; then echo "Cannot determine event count" 1>&2 exit 10 fi @@ -87,7 +87,7 @@ values() { do_value 'munin_error' 'ERROR' do_value 'munin_fatal' 'FATAL' # Set offset - "$logtail_bin" "$muninupdate" &> /dev/null + "$logtail_bin" "$muninupdate" > /dev/null 1>&2 chmod 640 "${muninupdate}.offset" } From c503e0dbcfd8ccffbe7e86c1437266baf53802c9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 17 Oct 2016 02:21:16 +0200 Subject: [PATCH 094/718] CI / Travis: add zsh syntax check --- .travis.yml | 2 +- t/test.t | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 76144bd2..4fb993af 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 python3 ruby php5-cli gawk ksh pylint + - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh zsh 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/t/test.t b/t/test.t index 7bc55d0b..b403062a 100644 --- a/t/test.t +++ b/t/test.t @@ -71,6 +71,14 @@ sub process_file { } ); } + elsif ( $interpreter =~ m{/bin/zsh} ) { + run_check( + { command => [ 'zsh', '-n', $file ], + description => 'zsh syntax check', + filename => $filename + } + ); + } elsif ( $interpreter =~ m{bash} ) { run_check( { command => [ 'bash', '-n', $file ], From b2a5b603e1523ea0f900d037376885e8535f1481 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 17 Oct 2016 02:52:08 +0200 Subject: [PATCH 095/718] [openvz_] Fix failure to monitor bean counters for servers with long names. This commit is based on https://github.com/munin-monitoring/contrib/pull/540, excluding the problematic change of the datasource names. --- plugins/openvz/openvz_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/openvz/openvz_ b/plugins/openvz/openvz_ index 4044ade7..a3223842 100755 --- a/plugins/openvz/openvz_ +++ b/plugins/openvz/openvz_ @@ -30,7 +30,7 @@ if [ "$1" = "config" ]; then echo "graph_vlabel $ATTRIBUTE Value" echo "graph_category openvz" echo "graph_info This graph shows OpenVZ: $ATTRIBUTE" - vzlist -a -H -o hostname | awk '{gsub(/\./,"_",$1) + vzlist -a -H --no-trim -o hostname | awk '{gsub(/\./,"_",$1) print("'$ATTRIBUTE'"$1".label "$1"\n" \ "'$ATTRIBUTE'"$1".info '$ATTRIBUTE' for VE"$1)}' exit 0 @@ -41,7 +41,7 @@ filter() { cat; } [ "$ATTRIBUTE" = 'status' ] && filter() { sed -e 's/running/10/g;s/stopped/0/g'; } #TODO: filter for uptime -vzlist -a -H -o hostname,$ATTRIBUTE | filter | awk '{gsub(/\./,"_",$1) +vzlist -a -H --no-trim -o hostname,$ATTRIBUTE | filter | awk '{gsub(/\./,"_",$1) print("'$ATTRIBUTE'"$1".value "$2)}' exit 0 From 74697839dd2f3a33a21514f2c7279a4017b8ef96 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 17 Oct 2016 02:55:39 +0200 Subject: [PATCH 096/718] [openvz_] Add real autoconf test --- plugins/openvz/openvz_ | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/openvz/openvz_ b/plugins/openvz/openvz_ index a3223842..4291a4bd 100755 --- a/plugins/openvz/openvz_ +++ b/plugins/openvz/openvz_ @@ -19,7 +19,11 @@ ATTRIBUTE=`basename $0 | sed 's/^openvz_//g'` if [ "$1" = "autoconf" ]; then - echo yes + if which vzlist >/dev/null; then + echo yes + else + echo "no (missing 'vzlist' executable)" + fi exit 0 fi @@ -45,4 +49,3 @@ vzlist -a -H --no-trim -o hostname,$ATTRIBUTE | filter | awk '{gsub(/\./,"_",$1) print("'$ATTRIBUTE'"$1".value "$2)}' exit 0 - From 1655f820334a10d953a96d0e568995d1ae5f5644 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 17 Oct 2016 02:59:11 +0200 Subject: [PATCH 097/718] [openvz_] Add 'suggest' capability --- plugins/openvz/openvz_ | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/openvz/openvz_ b/plugins/openvz/openvz_ index 4291a4bd..23f96b87 100755 --- a/plugins/openvz/openvz_ +++ b/plugins/openvz/openvz_ @@ -13,7 +13,7 @@ # $Log$ # #%# family=auto -#%# capabilities=autoconf +#%# capabilities=autoconf suggest ATTRIBUTE=`basename $0 | sed 's/^openvz_//g'` @@ -27,6 +27,12 @@ if [ "$1" = "autoconf" ]; then exit 0 fi +if [ "$1" = "suggest" ]; then + echo "laverage" + echo "status" + exit 0 +fi + if [ "$1" = "config" ]; then echo "graph_title $ATTRIBUTE" echo "graph_args --base 1000 -l 0" From 8b07fa2dc76eb9b381fe7f93ea4dff89ea7d1112 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Mon, 17 Oct 2016 03:03:46 +0200 Subject: [PATCH 098/718] [openvz_] Shell code style (quoting, backticks, indentation) --- plugins/openvz/openvz_ | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/openvz/openvz_ b/plugins/openvz/openvz_ index 23f96b87..3bd2b13f 100755 --- a/plugins/openvz/openvz_ +++ b/plugins/openvz/openvz_ @@ -15,7 +15,7 @@ #%# family=auto #%# capabilities=autoconf suggest -ATTRIBUTE=`basename $0 | sed 's/^openvz_//g'` +ATTRIBUTE=$(basename "$0" | sed 's/^openvz_//g') if [ "$1" = "autoconf" ]; then @@ -41,8 +41,8 @@ if [ "$1" = "config" ]; then echo "graph_category openvz" echo "graph_info This graph shows OpenVZ: $ATTRIBUTE" vzlist -a -H --no-trim -o hostname | awk '{gsub(/\./,"_",$1) - print("'$ATTRIBUTE'"$1".label "$1"\n" \ - "'$ATTRIBUTE'"$1".info '$ATTRIBUTE' for VE"$1)}' + print("'$ATTRIBUTE'"$1".label "$1"\n" \ + "'$ATTRIBUTE'"$1".info '$ATTRIBUTE' for VE"$1)}' exit 0 fi @@ -51,7 +51,7 @@ filter() { cat; } [ "$ATTRIBUTE" = 'status' ] && filter() { sed -e 's/running/10/g;s/stopped/0/g'; } #TODO: filter for uptime -vzlist -a -H --no-trim -o hostname,$ATTRIBUTE | filter | awk '{gsub(/\./,"_",$1) +vzlist -a -H --no-trim -o "hostname,$ATTRIBUTE" | filter | awk '{gsub(/\./,"_",$1) print("'$ATTRIBUTE'"$1".value "$2)}' exit 0 From 3a48fa4eabf0341415b9f5a92ab9461337993390 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Mon, 17 Oct 2016 09:57:02 +0200 Subject: [PATCH 099/718] added explaining comment for extra if If no LXD disk information is present, the field is None instead of an empty list. Iterating over it directly would fail. --- plugins/lxd/lxd_disk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/lxd/lxd_disk b/plugins/lxd/lxd_disk index 16366557..04988bbc 100755 --- a/plugins/lxd/lxd_disk +++ b/plugins/lxd/lxd_disk @@ -10,7 +10,7 @@ try: except: HAS_LIB=False errors.append("no pylxd module") - + c=None HAS_ACCESS=True try: @@ -39,7 +39,9 @@ if len(sys.argv) == 2 and sys.argv[1]=="config": 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(): info = c.container_info(name) + # first check if disk information list is available or None if info['disk']: + # if no disk information is present, this would fail to iteration None for disk in info['disk']: print(name+"-"+disk+".label "+name) print(name+"-"+disk+".draw LINE2") @@ -47,6 +49,8 @@ if len(sys.argv) == 2 and sys.argv[1]=="config": for name in c.container_list(): info = c.container_info(name) + # first check if disk information list is available or None if info['disk']: + # if no disk information is present, this would fail to iteration None for disk in info['disk']: print(name+"-"+disk+".value "+str(info['disk'][disk]['usage'])) From 114016b46110cfcf6cfd5ac838b831dac7319a1d Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Mon, 17 Oct 2016 20:08:06 +1100 Subject: [PATCH 100/718] [systemd] Warn on the first failed service Signed-off-by: Olivier Mehani --- plugins/system/systemd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/system/systemd b/plugins/system/systemd index 810c3b07..b12b7cc6 100755 --- a/plugins/system/systemd +++ b/plugins/system/systemd @@ -59,7 +59,7 @@ for state in $states; do echo "$state.label $state" echo "$state.draw AREASTACK" if [ $state = failed ]; then - echo "$state.warning 1" + echo "$state.warning 0" echo "$state.critical 10" fi done From a81a4660686156437556561ade1e242f6276a8e6 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Mon, 17 Oct 2016 20:13:28 +1100 Subject: [PATCH 101/718] [sickbeard_episodes] Fix label for snatched Signed-off-by: Olivier Mehani --- plugins/sickbeard/sickbeard_episodes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sickbeard/sickbeard_episodes b/plugins/sickbeard/sickbeard_episodes index 4ba5f6f7..decb9266 100755 --- a/plugins/sickbeard/sickbeard_episodes +++ b/plugins/sickbeard/sickbeard_episodes @@ -47,7 +47,7 @@ graph_title Episodes graph_vlabel Episodes graph_category Sick-Beard down.label Downloaded -down.snatched Snatched +snatched.label Snatched total.label Total EOC ; From 50783de9916208031229e36cfe51384671b7e61c Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Mon, 17 Oct 2016 09:53:58 -0400 Subject: [PATCH 102/718] senderbase licensing --- plugins/senderbase/senderbase | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/senderbase/senderbase b/plugins/senderbase/senderbase index d00f5d18..99d404cb 100755 --- a/plugins/senderbase/senderbase +++ b/plugins/senderbase/senderbase @@ -19,6 +19,12 @@ variable to the appropriate value. Phil! Gold +=head1 LICENSE + +This plugin is made available under a CC0 waiver: + + https://creativecommons.org/publicdomain/zero/1.0/ + =head1 MAGIC MARKERS #%# family=auto From 24ffbb8c29026d56633fbb1e208fe6c2d49f9131 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Mon, 17 Oct 2016 10:00:03 -0400 Subject: [PATCH 103/718] Document SenderBase in case people are unfamiliar with it. --- plugins/senderbase/senderbase | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/senderbase/senderbase b/plugins/senderbase/senderbase index 99d404cb..77a55bad 100755 --- a/plugins/senderbase/senderbase +++ b/plugins/senderbase/senderbase @@ -6,6 +6,14 @@ senderbase - Gives the current SenderBase reputation for a mail host. +SenderBase is a mail server reputation service run by Cisco. It's +basically a measure of how likely or unlikely they think it is for the +server in question to be sending spam, rated on a scale of -10 (near-100% +chance of spam) to 10 (near-0% chance of spam). The SenderBase score for +a server is one of several criteria that may be used when doing spam +filtering; consequently, it's useful for the operator of a mail server to +track their current reputation score. + =head1 CONFIGURATION By default, the script will use the first IP address returned by C. From 5355c24b491b84ea0ebeaad99c47b85a85a4241e Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Mon, 17 Oct 2016 10:04:03 -0400 Subject: [PATCH 104/718] `hostname -I` returns all values on one line. --- plugins/senderbase/senderbase | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/senderbase/senderbase b/plugins/senderbase/senderbase index 77a55bad..921c056f 100755 --- a/plugins/senderbase/senderbase +++ b/plugins/senderbase/senderbase @@ -16,9 +16,11 @@ track their current reputation score. =head1 CONFIGURATION -By default, the script will use the first IP address returned by C. -If that's not the right address to use, set the C environment -variable to the appropriate value. +By default, the script will use the first IP address returned by +C. If that's not the right address to use, set the +C environment variable to the appropriate value. (Note that +if a host has multiple IP addresses, there's no guaranteed ordering for +C, so you should set the address explicitly in that case.) [senderbase] env.ip_address 8.8.8.8 @@ -40,7 +42,7 @@ This plugin is made available under a CC0 waiver: =cut -real_ip_address=${ip_address:-$(hostname -I | head -1)} +real_ip_address=${ip_address:-$(hostname -I | awk '{print $1}')} ip_reversed=$(echo $real_ip_address | sed -re 's/^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/\4.\3.\2.\1/') query_host=${ip_reversed}.rf.senderbase.org From 2c09bd7de700e1f4b17a55e972690e6030f06d44 Mon Sep 17 00:00:00 2001 From: Phil! Gold Date: Mon, 17 Oct 2016 10:30:26 -0400 Subject: [PATCH 105/718] Check SenderBase with either dig or host, depending on what's available. --- plugins/senderbase/senderbase | 42 ++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/plugins/senderbase/senderbase b/plugins/senderbase/senderbase index 921c056f..88da9590 100755 --- a/plugins/senderbase/senderbase +++ b/plugins/senderbase/senderbase @@ -42,20 +42,36 @@ This plugin is made available under a CC0 waiver: =cut -real_ip_address=${ip_address:-$(hostname -I | awk '{print $1}')} -ip_reversed=$(echo $real_ip_address | sed -re 's/^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/\4.\3.\2.\1/') -query_host=${ip_reversed}.rf.senderbase.org +if which dig >/dev/null; then + query_program=dig + do_query () { + hostname=$1 + dig +short $hostname TXT | sed 's/"//g' | head -1 + } +elif which host >/dev/null; then + query_program=host + do_query () { + hostname=$1 + host -t TXT $hostname | grep -m 1 'descriptive text' | cut -d\" -f2 + } +else + query_program= + do_query () { + true + } +fi + +real_ip_address="${ip_address:-$(hostname -I | awk '{print $1}')}" +ip_reversed="$(echo $real_ip_address | sed -re 's/^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/\4.\3.\2.\1/')" +query_host="${ip_reversed}.rf.senderbase.org" if [ "$1" = "autoconf" ]; then - if which dig >/dev/null; then - value=$(dig +short $query_host TXT | sed 's/"//g' | head -1) - if [ -n "$value" ]; then - echo 'yes' - else - echo "no (No SenderBase reputation for IP address ${ip_address}.)" - fi + if [ -z "$query_program" ]; then + echo 'no (No "dig" or "host" executable found.)' + elif [ -z "$(do_query "$query_host")" ]; then + echo "no (No SenderBase reputation for IP address ${ip_address}.)" else - echo 'no (No "dig" executable found.)' + echo 'yes' fi exit 0 fi @@ -73,5 +89,5 @@ EOF exit 0 fi -value=$(dig +short $query_host TXT | sed 's/"//g' | head -1) -echo reputation.value ${value:-U} +value="$(do_query "$query_host")" +echo reputation.value "${value:-U}" From 9ec6beb0ec5b928863cf51f7dee9ff3172305c77 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 20 Oct 2016 03:01:23 +0200 Subject: [PATCH 106/718] Plugin boinc_projs: fix typo the problem was introduced in 1568bf67ea34b1ae59d3846a66f106ebce5702a4 --- plugins/boinc/boinc_projs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/boinc/boinc_projs b/plugins/boinc/boinc_projs index 8769b3be..f471c117 100755 --- a/plugins/boinc/boinc_projs +++ b/plugins/boinc/boinc_projs @@ -150,7 +150,7 @@ for my $rslt_info (@rsltInfos) { my $acttask = $acttask[0]; $acttask =~ s/^\s+active_task_state: //; if (( ($schedstat eq "2") && ($state eq "2") && ($acttask eq "1") ) || - ( ($schedstate eq "scheduled") && ($state eq "downloaded") && ($acttask eq "EXECUTING") )) { + ( ($schedstat eq "scheduled") && ($state eq "downloaded") && ($acttask eq "EXECUTING") )) { # This is running task $projects{$url}->{prj_running} += 1; } From de047e8f9029db878b1ee1e700261eeb880e6b64 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 20 Oct 2016 03:01:23 +0200 Subject: [PATCH 107/718] [boinc_projs] fix typo the problem was introduced in 1568bf67ea34b1ae59d3846a66f106ebce5702a4 --- plugins/boinc/boinc_projs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/boinc/boinc_projs b/plugins/boinc/boinc_projs index 8769b3be..f471c117 100755 --- a/plugins/boinc/boinc_projs +++ b/plugins/boinc/boinc_projs @@ -150,7 +150,7 @@ for my $rslt_info (@rsltInfos) { my $acttask = $acttask[0]; $acttask =~ s/^\s+active_task_state: //; if (( ($schedstat eq "2") && ($state eq "2") && ($acttask eq "1") ) || - ( ($schedstate eq "scheduled") && ($state eq "downloaded") && ($acttask eq "EXECUTING") )) { + ( ($schedstat eq "scheduled") && ($state eq "downloaded") && ($acttask eq "EXECUTING") )) { # This is running task $projects{$url}->{prj_running} += 1; } From 45e7dea7a10d3e2e2092e0272d705fc2cf34becc Mon Sep 17 00:00:00 2001 From: Peter Doherty Date: Sat, 16 Jul 2016 14:47:56 -0400 Subject: [PATCH 108/718] If a pool is named something like "m" or "ra" or any string that matches the types of zpool, the grep will match multiple lines. Adding a space after the zlabel will allow for more exact matching. --- plugins/zfs/zpool_iostat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/zfs/zpool_iostat b/plugins/zfs/zpool_iostat index 51c606e6..8149ef36 100755 --- a/plugins/zfs/zpool_iostat +++ b/plugins/zfs/zpool_iostat @@ -40,9 +40,9 @@ echo $zlist | tr ' ' '\n' | while read iz; do *) name=`echo $iz | gawk '{ gsub("[^a-zA-Z0-9_]", "_", $1); print $1 }'` ;; esac echo -n $name'_read.value ' - grep '^[ ]*'$zlabel $ztmp|gawk '{print $6}'|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' + grep '^[ ]*'$zlabel\ $ztmp|gawk '{print $6}'|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' echo -n $name'_write.value ' - grep '^[ ]*'$zlabel $ztmp|gawk '{print $7}'|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' + grep '^[ ]*'$zlabel\ $ztmp|gawk '{print $7}'|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' done rm $ztmp; touch $ztmp From 50537fa5b940afc8ba9876a22806ad830d1f01be Mon Sep 17 00:00:00 2001 From: Peter Doherty Date: Sun, 16 Oct 2016 19:08:49 -0400 Subject: [PATCH 109/718] improved readability, and eliminate a grep --- plugins/zfs/zpool_iostat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/zfs/zpool_iostat b/plugins/zfs/zpool_iostat index 8149ef36..a2e2c22a 100755 --- a/plugins/zfs/zpool_iostat +++ b/plugins/zfs/zpool_iostat @@ -40,9 +40,9 @@ echo $zlist | tr ' ' '\n' | while read iz; do *) name=`echo $iz | gawk '{ gsub("[^a-zA-Z0-9_]", "_", $1); print $1 }'` ;; esac echo -n $name'_read.value ' - grep '^[ ]*'$zlabel\ $ztmp|gawk '{print $6}'|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' + gawk '{ if ($1 == "'"$zlabel"'") print $6; }' "$ztmp"|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' echo -n $name'_write.value ' - grep '^[ ]*'$zlabel\ $ztmp|gawk '{print $7}'|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' + gawk '{ if ($1 == "'"$zlabel"'") print $7; }' "$ztmp"|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' done rm $ztmp; touch $ztmp From 53135f5f5d187a04075ac724c03c08d5dc672dc0 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 12 Jun 2016 02:51:52 +0200 Subject: [PATCH 110/718] network: new wifi_signal_noise_ plugin for signal strength and noise This new plugin offers the following advantages compared to the existing wifi_signal plugin: * support multiple wifi peers (e.g. mesh mode or master mode with multiple clients) * use "iwinfo" or "iw" for information retrieval (instead of Linux's procfs) --- plugins/network/wifi_signal_noise_ | 108 +++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100755 plugins/network/wifi_signal_noise_ diff --git a/plugins/network/wifi_signal_noise_ b/plugins/network/wifi_signal_noise_ new file mode 100755 index 00000000..e4c2b946 --- /dev/null +++ b/plugins/network/wifi_signal_noise_ @@ -0,0 +1,108 @@ +#!/bin/sh +# +# Show current signal strength and noise for all connected peers of wifi devices. +# This plugin is suitable for wifi interfaces with a stable selection of peers +# (e.g. infrastructure). +# Author: Lars Kruse, devel@sumpfralle.de +# License: GPL v3 or later +# +# Requirements: +# * "iwinfo" tool (alternatively: fall back to "iw" - with incomplete data) +# * root privileges (for "iw" and "iwinfo") +# +# Magic markers +#%# capabilities=autoconf suggest +#%# family=auto + + +set -eu + + +# prefer "iwinfo" for information retrieval, if it is available +if which iwinfo >/dev/null; then + # "iwinfo" has a stable output format but is only available on openwrt + get_wifi_interfaces() { iwinfo | grep "^[a-zA-Z]" | awk '{print $1}'; } + # return MAC of peer and the signal strength + get_wifi_peers() { iwinfo "$1" assoclist | grep "^[0-9a-fA-F]" | awk '{print $1,$2}'; } + # the noise should be the same for all peers + get_wifi_noise() { iwinfo "$1" info | sed -n 's/^.* Noise: \([0-9-]\+\).*/\1/p'; } +else + # "iw" is available everywhere - but its output format is not recommended for non-humans + get_wifi_interfaces() { iw dev | awk '{ if ($1 == "Interface") print $2; }'; } + get_wifi_peers() { iw dev wlan0 station dump \ + | awk '{ if ($1 == "Station") mac=$2; if (($1 == "signal") && ($2 == "avg:")) print mac,$3}'; } + # TODO: there seems to be no way to retrieve the noise level via "iw" + get_wifi_noise() { echo; } +fi + + +clean_fieldname() { + echo "$1" | sed 's/^\([^A-Za-z_]\)/_\1/; s/[^A-Za-z0-9_]/_/g' +} + + +get_ip_for_mac() { + local ip + ip=$(arp -n | grep -iw "$1$" | awk '{print $1}' | sort | head -1) + [ -n "$ip" ] && echo "$ip" && return 0 + # no IP found - return MAC instead + echo "$1" +} + + +get_wifi_device_from_suffix() { + local suffix + local real_dev + # pick the part after the basename of the real file + suffix=$(basename "$0" | sed "s/^$(basename "$(readlink "$0")")//") + for real_dev in $(get_wifi_interfaces); do + [ "$suffix" != "$(clean_fieldname "$real_dev")" ] || echo "$real_dev" + done | head -1 +} + + +ACTION="${1:-}" + +case "$ACTION" in + config) + wifi=$(get_wifi_device_from_suffix) + echo "graph_title Wireless signal quality - $wifi" + echo "graph_args --upper-limit 0" + echo "graph_vlabel Signal and noise [dBm]" + echo "graph_category network" + echo "graph_info This graph shows the signal and noise for all wifi peers" + echo "noise.label Noise floor" + echo "noise.draw LINE" + # sub graphs for all peers + get_wifi_peers "$wifi" | while read mac signal; do + fieldname=$(clean_fieldname "peer_${mac}") + peer=$(get_ip_for_mac "$mac") + echo "signal_${fieldname}.label $peer" + echo "signal_${fieldname}.draw LINE" + done + ;; + autoconf) + [ -z "$(get_wifi_interfaces)" ] && echo "no (no wifi interfaces found)" && exit 1 + echo "yes" + ;; + suggest) + get_wifi_interfaces | while read ifname; do + clean_fieldname "$ifname" + done + ;; + "") + wifi=$(get_wifi_device_from_suffix) + peer_data=$(get_wifi_peers "$wifi") + echo "$peer_data" | while read mac signal; do + # ignore empty datasets + [ -z "$signal" ] && continue + fieldname=$(clean_fieldname "peer_${mac}") + echo "signal_${fieldname}.value $signal" + done + echo "noise.value $(get_wifi_noise "$wifi")" + ;; + *) + echo >&2 "Invalid action (valid: config)" + echo >&2 + ;; +esac From 78f4e42d21958963ad893742110a16a292b97ecb Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 01:48:36 +0200 Subject: [PATCH 111/718] [cpubyuser] use 'clean_fieldname' instead of incomplete substitution (Closes: #757) --- plugins/system/cpubyuser | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index c2522233..d5a42a11 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -37,6 +37,8 @@ #%# family=auto #%# capabilities=autoconf +. "$MUNIN_LIBDIR/plugins/plugin.sh" + if [ "$1" = "autoconf" ]; then if [ -n "$USERS" ]; then echo "yes" @@ -54,11 +56,11 @@ if [ "$1" = "config" ]; then echo "graph_vlabel %" echo "graph_scale no" echo "graph_period second" - _USERS=${USERS//[-.]/_} + _USERS="$(for user in $USERS; do clean_fieldname "$user" | tr '\n' ' '; done)" echo "graph_order $_USERS others" FIRSTUSER=1; for USER in $USERS "others"; do - _USER=${USER//[-.]/_} + _USER="$(clean_fieldname "$USER")" echo "${_USER}.label $USER" echo "${_USER}.info CPU used by user $USER" echo "${_USER}.type GAUGE" From a46588050103e8816e415ebc24b3ac7d616cc07d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 01:54:24 +0200 Subject: [PATCH 112/718] [cpubyuser] simplify AREASTACK usage --- plugins/system/cpubyuser | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index d5a42a11..5c1020a7 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -58,18 +58,12 @@ if [ "$1" = "config" ]; then echo "graph_period second" _USERS="$(for user in $USERS; do clean_fieldname "$user" | tr '\n' ' '; done)" echo "graph_order $_USERS others" - FIRSTUSER=1; for USER in $USERS "others"; do _USER="$(clean_fieldname "$USER")" echo "${_USER}.label $USER" echo "${_USER}.info CPU used by user $USER" echo "${_USER}.type GAUGE" - if [ $FIRSTUSER -eq 1 ]; then - echo "${_USER}.draw AREA" - FIRSTUSER=0 - else - echo "${_USER}.draw STACK" - fi + echo "${_USER}.draw AREASTACK" done exit fi From 002e8bbf996ce0c8038d8efd97c268c0cb575c56 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 01:54:57 +0200 Subject: [PATCH 113/718] [cpubyuser] clear variable names --- plugins/system/cpubyuser | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index 5c1020a7..f2c548f1 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -56,14 +56,14 @@ if [ "$1" = "config" ]; then echo "graph_vlabel %" echo "graph_scale no" echo "graph_period second" - _USERS="$(for user in $USERS; do clean_fieldname "$user" | tr '\n' ' '; done)" - echo "graph_order $_USERS others" - for USER in $USERS "others"; do - _USER="$(clean_fieldname "$USER")" - echo "${_USER}.label $USER" - echo "${_USER}.info CPU used by user $USER" - echo "${_USER}.type GAUGE" - echo "${_USER}.draw AREASTACK" + user_fields="$(for user in $USERS; do clean_fieldname "$user" | tr '\n' ' '; done)" + echo "graph_order $user_fields others" + for user in $USERS "others"; do + user_field="$(clean_fieldname "$user")" + echo "${user_field}.label $user" + echo "${user_field}.info CPU used by user $user" + echo "${user_field}.type GAUGE" + echo "${user_field}.draw AREASTACK" done exit fi From 6f7473b2a7b26912565233530b13f2da0de7dc45 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 02:09:43 +0200 Subject: [PATCH 114/718] [cpubyuser] proper 'no' output for autoconf --- plugins/system/cpubyuser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index f2c548f1..04dc2c2f 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -43,7 +43,7 @@ if [ "$1" = "autoconf" ]; then if [ -n "$USERS" ]; then echo "yes" else - echo "\$USERS not defined." + echo "no (USERS setting is missing)" fi exit fi From 4b21d5819537416c7f88965036abc7fc7425a422 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 03:50:32 +0200 Subject: [PATCH 115/718] [cpubyuser] expand magic 'ALL' user before config --- plugins/system/cpubyuser | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index 04dc2c2f..39469981 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -39,6 +39,9 @@ . "$MUNIN_LIBDIR/plugins/plugin.sh" +[ "$USERS" = "ALL" ] && USERS=$(w --no-header | awk '{ print $1 }' | sort | uniq) + + if [ "$1" = "autoconf" ]; then if [ -n "$USERS" ]; then echo "yes" @@ -68,10 +71,6 @@ if [ "$1" = "config" ]; then exit fi -if [ "$USERS" = "ALL" ]; then - USERS=$( w | awk '{ print $1 }' ) -fi - top -b -n 1 | tail -n +8 | \ awk -v USERS="$USERS" ' { if ($2 != "USER") CPU_USER[$2]+=$9 } From 391a4bfa416540a1e2aff48b6e296b595805d9d3 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 03:52:05 +0200 Subject: [PATCH 116/718] [cpubyuser] user constant for magic 'others' user --- plugins/system/cpubyuser | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index 39469981..096a32c0 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -39,6 +39,7 @@ . "$MUNIN_LIBDIR/plugins/plugin.sh" +OTHER_FIELD="others" [ "$USERS" = "ALL" ] && USERS=$(w --no-header | awk '{ print $1 }' | sort | uniq) @@ -60,8 +61,8 @@ if [ "$1" = "config" ]; then echo "graph_scale no" echo "graph_period second" user_fields="$(for user in $USERS; do clean_fieldname "$user" | tr '\n' ' '; done)" - echo "graph_order $user_fields others" - for user in $USERS "others"; do + echo "graph_order $user_fields $OTHER_FIELD" + for user in $USERS "$OTHER_FIELD"; do user_field="$(clean_fieldname "$user")" echo "${user_field}.label $user" echo "${user_field}.info CPU used by user $user" From 5bec8ec6b59099f678f24d2a16a160c1912cc13c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 03:55:45 +0200 Subject: [PATCH 117/718] [cpubyuser] improved top output without header --- plugins/system/cpubyuser | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index 096a32c0..26fed6e4 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -72,9 +72,9 @@ if [ "$1" = "config" ]; then exit fi -top -b -n 1 | tail -n +8 | \ +top -b -n 1 | sed '1,/^ *PID /d' | \ awk -v USERS="$USERS" ' - { if ($2 != "USER") CPU_USER[$2]+=$9 } + { CPU_USER[$2]+=$9 } END { others_sum = 0 for (user in CPU_USER) { From 8280dbb0f5050b077453947daa60dc5d3ca39b60 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 03:59:22 +0200 Subject: [PATCH 118/718] [cpubyuser] improve parsing for long usernames (Closes: #757) "ps" abbreviates long username - thus we prepare a separate mapping --- plugins/system/cpubyuser | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index 26fed6e4..2931962c 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -74,17 +74,32 @@ fi top -b -n 1 | sed '1,/^ *PID /d' | \ awk -v USERS="$USERS" ' - { CPU_USER[$2]+=$9 } + # Store the CPU usage of each process - the mapping to the + # user happens later. We cannot use the second column + # (username) directly, since it may be abbreviated (ending + # with "+"). + { CPU_PER_PID[$1]=$9 } END { - others_sum = 0 - for (user in CPU_USER) { - m = match(USERS,user) - if (m != 0) { - _user=user - gsub(/[-.]/,"_", _user); - print _user".value", CPU_USER[user] - } else - others_sum += CPU_USER[user] + split(USERS, user_array) + for (user_index in user_array) { + user = user_array[user_index] + # retrieve all process IDs belonging to the user + "ps -u "user" -o pid --no-headers 2>/dev/null | tr \"\n\" \" \"" | getline pids + user_cpu = 0 + split(pids, pid_array) + # summarize the cpu usage of this usage + for (pid_index in pid_array) { + pid = pid_array[pid_index] + user_cpu += CPU_PER_PID[pid] + delete CPU_PER_PID[pid] + } + print user, user_cpu } - print "others.value", others_sum; - }' + # add all remaining cpu usages into "others" + others_sum = 0 + for (other_usage in CPU_PER_PID) others_sum+=CPU_PER_PID[other_usage] + print "'"$OTHER_FIELD"'", others_sum; + }' | while read -r user count; do + # apply fieldname cleanup + echo "$(clean_fieldname "$user").value $count" + done From 85d91b50f4c3a6d2808a0b4f302c792cba0c119b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 04:02:49 +0200 Subject: [PATCH 119/718] [cpubyuser] fix whitespace --- plugins/system/cpubyuser | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index 2931962c..0f7a2cc3 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -2,12 +2,12 @@ # # Plugin to monitor CPU usage, for a selected set of users # -# Usage: Place in /etc/munin/node.d/ (or link it there using ln -s) -# Add this to your /etc/munin/plugin-conf.d/munin-node: +# Usage: Place in /etc/munin/node.d/ (or link it there using ln -s) +# Add this to your /etc/munin/plugin-conf.d/munin-node: # [cpubyuser] # env.USERS root yann # -# If env.USERS is set to ALL, count all logged in users. +# If env.USERS is set to ALL, count all logged in users. # # root and yann being a list of the users to monitor. # You need to also make sure that awk is installed @@ -30,8 +30,8 @@ # # Parameters understood: # -# config (required) -# autoconf (optional - used by munin-config) +# config (required) +# autoconf (optional - used by munin-config) # #%# family=auto @@ -63,7 +63,7 @@ if [ "$1" = "config" ]; then user_fields="$(for user in $USERS; do clean_fieldname "$user" | tr '\n' ' '; done)" echo "graph_order $user_fields $OTHER_FIELD" for user in $USERS "$OTHER_FIELD"; do - user_field="$(clean_fieldname "$user")" + user_field="$(clean_fieldname "$user")" echo "${user_field}.label $user" echo "${user_field}.info CPU used by user $user" echo "${user_field}.type GAUGE" From 2db4f2ca5654cfc4e2e19691e7c63bc7004dbfa4 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 04:03:35 +0200 Subject: [PATCH 120/718] [cpubyuser] switch from bash to sh --- plugins/system/cpubyuser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/system/cpubyuser b/plugins/system/cpubyuser index 0f7a2cc3..77d599f5 100755 --- a/plugins/system/cpubyuser +++ b/plugins/system/cpubyuser @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Plugin to monitor CPU usage, for a selected set of users # From d6ea0c926da27fb11fb1d807bc06c7faba5623d2 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 13:11:09 +0200 Subject: [PATCH 121/718] [mpdstats_] remove "-q" from netcat call (Closes: 576) --- plugins/mpd/mpdstats_ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mpd/mpdstats_ b/plugins/mpd/mpdstats_ index ec78ad4e..b54edfdc 100755 --- a/plugins/mpd/mpdstats_ +++ b/plugins/mpd/mpdstats_ @@ -90,7 +90,7 @@ do_autoconf () { exit 1 fi - echo version | $NCBIN -q 2 $MPDHOST $MPDPORT 1>/dev/null 2>&1 + echo version | "$NCBIN" "$MPDHOST" "$MPDPORT" 1>/dev/null 2>&1 if [ "$?" != 0 ] ; then echo "no (connection failed)" exit 1 @@ -129,6 +129,6 @@ $ACTION.label $ACTION" ;; *) printf "$ACTION.value " - echo stats | $NCBIN -q 2 $MPDHOST $MPDPORT | awk " /^${ACTION}/ {print \$2}" + echo stats | "$NCBIN" "$MPDHOST" "$MPDPORT" | awk "/^${ACTION}:/ {print \$2}" ;; esac From 380c4a9ea557eb1d70dd347c5972c18d0488d4e9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 13:41:58 +0200 Subject: [PATCH 122/718] [mpdstats_] use 'which' for finding 'nc' --- plugins/mpd/mpdstats_ | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/mpd/mpdstats_ b/plugins/mpd/mpdstats_ index b54edfdc..65f9194f 100755 --- a/plugins/mpd/mpdstats_ +++ b/plugins/mpd/mpdstats_ @@ -69,7 +69,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. MPDHOST=${mpd_host:-localhost} MPDPORT=${mpd_port:-6600} -NCBIN=${netcat:-/bin/nc} +NCBIN=${netcat:-$(which nc)} + # # FUNCTIONS @@ -85,8 +86,8 @@ do_autoconf () { ;; esac - if [ ! -x $NCBIN ] ; then - echo "no ($NCBIN: not found or not executable)" + if [ -z "$NCBIN" ] ; then + echo "no (missing netcat program ('nc'))" exit 1 fi From 09d9f32e716e3261de039cc7d1a3d8e0a817f127 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 22 Oct 2016 13:43:31 +0200 Subject: [PATCH 123/718] [mpdstats_] allow unconfigured usage (all stats) --- plugins/mpd/mpdstats_ | 82 ++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/plugins/mpd/mpdstats_ b/plugins/mpd/mpdstats_ index 65f9194f..35381fa9 100755 --- a/plugins/mpd/mpdstats_ +++ b/plugins/mpd/mpdstats_ @@ -19,8 +19,10 @@ work like a charm already. =head1 INSTALLATION -This wildcard plugin should be symlinked using one of these names: -mpdstats_artists, mpdstats_albums, mpdstats_songs +This wildcard plugin can be symlinked using one of the statistic +categories of mpd. See the output of "echo stats | nc MPD_HOST 6600". +Without a symlink configuration (no suffix after the underscore) all +stats are shown. =head1 CONFIGURATION @@ -66,33 +68,25 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut +. "$MUNIN_LIBDIR/plugins/plugin.sh" MPDHOST=${mpd_host:-localhost} MPDPORT=${mpd_port:-6600} NCBIN=${netcat:-$(which nc)} +ACTION="$(basename "$0" | sed 's/^.*_//')" # # FUNCTIONS # do_autoconf () { - case "$ACTION" in - albums|artists|songs) - ;; - *) - echo "no (not a valid symlink name, please read the plugin doc)" - exit 1 - ;; - esac - if [ -z "$NCBIN" ] ; then echo "no (missing netcat program ('nc'))" exit 1 fi - echo version | "$NCBIN" "$MPDHOST" "$MPDPORT" 1>/dev/null 2>&1 - if [ "$?" != 0 ] ; then + if ! echo version | "$NCBIN" "$MPDHOST" "$MPDPORT" >/dev/null 2>&1; then echo "no (connection failed)" exit 1 fi @@ -101,35 +95,65 @@ do_autoconf () { exit 0 } + +get_mpd_stats_keys() { + echo stats | "$NCBIN" "$MPDHOST" "$MPDPORT" | awk 'BEGIN {FS=":"} /^[^ ]+:/ {print $1}' +} + + +print_mpd_stat_value() { + local key="$1" + local fieldname + local stat_value + fieldname="$(clean_fieldname "$key")" + stat_value="$(echo stats | "$NCBIN" "$MPDHOST" "$MPDPORT" | awk "/^${key}:/ {print \$2}")" + echo "${fieldname}.value $stat_value" +} + + # # MAIN # -SCRIPTNAME=${0##*/} -ACTION=${SCRIPTNAME##*_} case "$1" in autoconf) do_autoconf ;; suggest) - echo "$ACTION" - exit 0 + get_mpd_stats_keys ;; config) - echo "graph_category MPD -graph_title $ACTION in the database -graph_info Number of $ACTION in the database. -graph_vlabel $ACTION in the db -graph_args --base 1000 -l 0 -graph_scale no -$ACTION.type GAUGE -$ACTION.draw LINE2 -$ACTION.label $ACTION" - exit 0 + echo "graph_category MPD" + echo "graph_args --base 1000 -l 0" + echo "graph_scale no" + if [ -z "$ACTION" ]; then + echo "graph_title MPD Statistics" + echo "graph_vlabel number of items" + for stat_key in $(get_mpd_stats_keys); do + fieldname="$(clean_fieldname "$stat_key")" + echo "${fieldname}.type GAUGE" + echo "${fieldname}.draw LINE" + echo "${fieldname}.label $stat_key" + done + else + # Show only a single stat + echo "graph_title $ACTION in the MPD database" + echo "graph_info Number of $ACTION in the database." + echo "graph_vlabel $ACTION in the db" + echo "$ACTION.type GAUGE" + echo "$ACTION.draw LINE2" + echo "$ACTION.label $ACTION" + fi ;; *) - printf "$ACTION.value " - echo stats | "$NCBIN" "$MPDHOST" "$MPDPORT" | awk "/^${ACTION}:/ {print \$2}" + if [ -z "$ACTION" ]; then + for stat_key in $(get_mpd_stats_keys); do + print_mpd_stat_value "$stat_key" + done + else + print_mpd_stat_value "$ACTION" + fi ;; esac +exit 0 From e7bcfec037cd2f17fcd82e9e381ae36ef972159f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 22 Oct 2016 18:44:20 +0000 Subject: [PATCH 124/718] Modify munin_events following intrustions from @sumpfralle --- plugins/munin/munin_events | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/munin/munin_events b/plugins/munin/munin_events index 26498de3..2a355bd9 100755 --- a/plugins/munin/munin_events +++ b/plugins/munin/munin_events @@ -39,7 +39,7 @@ INFO, WARNING, ERROR, FATAL. =head1 VERSION - 1.2.20161017 + 1.2.20160514 =head1 AUTHOR @@ -70,9 +70,9 @@ logtail_bin=${logtail_bin:-/usr/sbin/logtail2} do_value() { FIELD="$1" EVENT_LABEL="$2" - EVENT_COUNT="$("$logtail_bin" -t "$muninupdate" 2> /dev/null | grep -c "^[0-9/: ]\{19\} \[${EVENT_LABEL}\]")" - if ! [ -z "$(echo "$EVENT_COUNT" | sed 's|[0-9]\+||')" ]; then + EVENT_COUNT="$("$logtail_bin" -t "$muninupdate" 2> /dev/null | grep -c "^[0-9/: ]\{19\} \[${EVENT_LABEL}\]")" + if echo "$EVENT_COUNT" | grep -q "[^0-9]"; then echo "Cannot determine event count" 1>&2 exit 10 fi From 0f24fe00eb77a70fa38c78dfa017f78d2fdcedca Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 23 Oct 2016 00:56:39 +0200 Subject: [PATCH 125/718] [monit_parser] fix whitespace issues --- plugins/monit/monit_parser | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index f89bd4db..599f57a8 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -17,7 +17,7 @@ def sanitize(s): out = str() for char in s: if char.lower() in OK_CHARS: - out += char + out += char return out procs = dict() @@ -33,20 +33,20 @@ for line in output: m = re.match("^Process '(.*)'.*$", line) if m: cur_proc = sanitize(m.group(1)) - try: - procs[cur_proc] - except KeyError: - procs[cur_proc] = dict() - continue + try: + procs[cur_proc] + except KeyError: + procs[cur_proc] = dict() + continue m = re.match(" memory kilobytes total\s+([0-9]+).*$", line) if m: procs[cur_proc]["total_memory"] = m.group(1) - continue + continue m = re.match(" cpu percent total\s+([.0-9]+)%.*$", line) if m: procs[cur_proc]["total_cpu"] = m.group(1) - continue - + continue + if len(sys.argv) > 1 and sys.argv[1] == 'config': print 'graph_title Per process stats from Monit' print 'graph_vlabel numbers' @@ -55,7 +55,7 @@ if len(sys.argv) > 1 and sys.argv[1] == 'config': 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) + print "monit_%s_%s.warning 1:" % (process, stat) sys.exit(0) for process in procs: From 9d62e558969631d8272881edec49183033fe3657 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 23 Oct 2016 01:27:25 +0200 Subject: [PATCH 126/718] [monit_parser] replace 'commands' module with 'subprocess' necessary for python3 support --- plugins/monit/monit_parser | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index 599f57a8..49c25330 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -8,9 +8,11 @@ STATUS_CMD = "monit status" -import sys +import os import re -from commands import getstatusoutput +import subprocess +import sys + def sanitize(s): OK_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789" @@ -22,12 +24,16 @@ def sanitize(s): procs = dict() -status, output = getstatusoutput(STATUS_CMD) -if status != 0: - print "Errror running status command: %s" % (output) - sys.exit(status) +try: + output = subprocess.check_output(STATUS_CMD.split()) +except (OSError, subprocess.CalledProcessError) as exc: + sys.stderr.write("Error running monit status command: %s%s" % (exc, os.linesep)) + sys.exit(1) -output = output.split('\n') +# python3: the result of command execution is bytes and needs to be converted +if not isinstance(output, str): + output = output.decode("ascii", "ignore") +output = output.splitlines() cur_proc = None for line in output: m = re.match("^Process '(.*)'.*$", line) From dcd9434e0dd5323cf6f178af35298a82216eb524 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 23 Oct 2016 01:27:50 +0200 Subject: [PATCH 127/718] [monit_parser] use python2/3 compatible 'print' style --- plugins/monit/monit_parser | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index 49c25330..228f3652 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -54,16 +54,16 @@ for line in output: continue if len(sys.argv) > 1 and sys.argv[1] == 'config': - print 'graph_title Per process stats from Monit' - print 'graph_vlabel numbers' - print 'graph_category monit' + print('graph_title Per process stats from Monit') + print('graph_vlabel numbers') + print('graph_category monit') for process in procs: for stat in procs[process]: - print "monit_%s_%s.label %s.%s" % (process, stat, process, stat) + print("monit_%s_%s.label %s.%s" % (process, stat, process, stat)) if stat == 'total_memory': - print "monit_%s_%s.warning 1:" % (process, stat) + print("monit_%s_%s.warning 1:" % (process, stat)) sys.exit(0) for process in procs: for stat in procs[process]: - print "monit_%s_%s.value %s" % (process, stat, procs[process][stat]) + print("monit_%s_%s.value %s" % (process, stat, procs[process][stat])) From 8d78e459cd00875915537f440f3da6fdd2902a3c Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 23 Oct 2016 03:14:48 +0200 Subject: [PATCH 128/718] [monit_parser] add documentation (Closes: #548) --- plugins/monit/monit_parser | 43 +++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index 228f3652..b4e2facc 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -1,12 +1,45 @@ #!/usr/bin/python -# Monit status parser plugin for munin -# This is very raw, but it should work for you out of the box. You of course -# need to have monit configured such that the 'monit status' command works. +""" +=head1 NAME -# Todd Troxell +monit_parser - Monit status parser plugin for munin. -STATUS_CMD = "monit status" + +=head1 APPLICABLE SYSTEMS + +Any system running monit. + +Monit needs to be configured with the httpd port enabled, e.g.: + + set httpd port 2812 + +The configured port is not important, since this plugin uses "monit status" +for retrieving data. Thus the monit configuration is parsed implicitly. + + +=head1 CONFIGURATION + +This plugin requires read access for the monit configuration. +Thus it needs to run as root: + + [monit_parser] + user root + + +=head1 AUTHOR + + Todd Troxell + Lars Kruse + + +=head1 MAGIC MARKERS + + family=auto + capabilities=autoconf + +=cut +""" import os import re From e5fbbaea5fd2c2b0d560f5bacc45be7e915b84c9 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 23 Oct 2016 03:20:05 +0200 Subject: [PATCH 129/718] [monit_parser] improve code structure --- plugins/monit/monit_parser | 94 +++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index b4e2facc..3515f977 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -41,7 +41,6 @@ Thus it needs to run as root: =cut """ -import os import re import subprocess import sys @@ -49,44 +48,57 @@ import sys def sanitize(s): OK_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789" - out = str() - for char in s: - if char.lower() in OK_CHARS: - out += char - return out + return "".join([char for char in s if char in OK_CHARS]) -procs = dict() -try: - output = subprocess.check_output(STATUS_CMD.split()) -except (OSError, subprocess.CalledProcessError) as exc: - sys.stderr.write("Error running monit status command: %s%s" % (exc, os.linesep)) - sys.exit(1) +def get_monit_status_lines(): + # retrieve output + try: + output = subprocess.check_output(["monit", "status"]) + error_message = None + except (OSError, subprocess.CalledProcessError) as exc: + error_message = "Error running monit status command: %s" % str(exc) + if error_message is not None: + raise OSError(error_message) + # python3: the result of command execution is bytes and needs to be converted + if not isinstance(output, str): + output = output.decode("ascii", "ignore") + return output.splitlines() -# python3: the result of command execution is bytes and needs to be converted -if not isinstance(output, str): - output = output.decode("ascii", "ignore") -output = output.splitlines() -cur_proc = None -for line in output: - m = re.match("^Process '(.*)'.*$", line) - if m: - cur_proc = sanitize(m.group(1)) - try: - procs[cur_proc] - except KeyError: - procs[cur_proc] = dict() - continue - m = re.match(" memory kilobytes total\s+([0-9]+).*$", line) - if m: - procs[cur_proc]["total_memory"] = m.group(1) - continue - m = re.match(" cpu percent total\s+([.0-9]+)%.*$", line) - if m: - procs[cur_proc]["total_cpu"] = m.group(1) - continue -if len(sys.argv) > 1 and sys.argv[1] == 'config': +def parse_processes(): + cur_proc = None + procs = {} + for line in get_monit_status_lines(): + m = re.match("^Process '(.*)'.*$", line) + if m: + cur_proc = sanitize(m.group(1)) + try: + procs[cur_proc] + except KeyError: + procs[cur_proc] = {} + continue + m = re.match(" memory kilobytes total\s+([0-9]+).*$", line) + if m: + procs[cur_proc]["total_memory"] = m.group(1) + continue + m = re.match(" cpu percent total\s+([.0-9]+)%.*$", line) + if m: + procs[cur_proc]["total_cpu"] = m.group(1) + continue + return procs + + +action = sys.argv[1] if (len(sys.argv) > 1) else None + +if action == 'autoconf': + try: + get_monit_status_lines() + print("yes") + except OSError: + print("no (failed to request monit status)") +elif action == 'config': + procs = parse_processes() print('graph_title Per process stats from Monit') print('graph_vlabel numbers') print('graph_category monit') @@ -94,9 +106,9 @@ if len(sys.argv) > 1 and sys.argv[1] == 'config': 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: - for stat in procs[process]: - print("monit_%s_%s.value %s" % (process, stat, procs[process][stat])) + # the allocated memory may never be zero + print("monit_%s_%s.warning 1:" % (process, stat)) +else: + for process, stats in parse_processes().items(): + for stat_key, stat_value in stats.items(): + print("monit_%s_%s.value %s" % (process, stat_key, stat_value)) From b8f1b6c87980772e10c2a55d12dbe9f8bf9c1d1d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 23 Oct 2016 03:40:44 +0200 Subject: [PATCH 130/718] [monit_parser] implement memory alloaction unit parsing Previously monit seems to have output memory usage in kilobytes without a unit. Somewhen this seemed to have changed to an (optional?) suffix (e.g. MB). Thus the new scaling may differ from the previous scaling, which was probably broken for most users. Additionally the units of the resulting values are clarified. --- plugins/monit/monit_parser | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index 3515f977..7306f612 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -78,9 +78,21 @@ def parse_processes(): except KeyError: procs[cur_proc] = {} continue - m = re.match(" memory kilobytes total\s+([0-9]+).*$", line) + m = re.match(" memory kilobytes total\s+([0-9.]+)(.*)$", line) if m: - procs[cur_proc]["total_memory"] = m.group(1) + size, suffix = m.groups() + # we store memory consumption as megabytes + factor = { + "": 1024 ** -1, + "KB": 1024 ** -1, + "MB": 1024 ** 0, + "GB": 1024 ** 1, + "TB": 1024 ** 2, + }[suffix.strip().upper()] + try: + procs[cur_proc]["total_memory"] = float(size) * factor + except ValueError: + procs[cur_proc]["total_memory"] = "U" continue m = re.match(" cpu percent total\s+([.0-9]+)%.*$", line) if m: @@ -100,7 +112,7 @@ if action == 'autoconf': elif action == 'config': procs = parse_processes() print('graph_title Per process stats from Monit') - print('graph_vlabel numbers') + print('graph_vlabel usage of memory [MB] or cpu [%]') print('graph_category monit') for process in procs: for stat in procs[process]: From ef6cedf7a918199ea960f9a96395fa4fda1111ce Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 23 Oct 2016 05:05:28 +0200 Subject: [PATCH 131/718] [monit_parser] change input format to XML; retrieve via URL (Closes: #558) --- plugins/monit/monit_parser | 96 ++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 57 deletions(-) diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index 7306f612..67a2b767 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 """ =head1 NAME @@ -8,23 +8,22 @@ monit_parser - Monit status parser plugin for munin. =head1 APPLICABLE SYSTEMS -Any system running monit. +Any system being able to query monit servers via http. Monit needs to be configured with the httpd port enabled, e.g.: set httpd port 2812 -The configured port is not important, since this plugin uses "monit status" -for retrieving data. Thus the monit configuration is parsed implicitly. + =head1 CONFIGURATION -This plugin requires read access for the monit configuration. -Thus it needs to run as root: +By default the monit instance at localhost is queried: [monit_parser] - user root + env.port 2812 + env.host localhost =head1 AUTHOR @@ -41,9 +40,14 @@ Thus it needs to run as root: =cut """ -import re -import subprocess +import xml.dom.minidom +import os import sys +import urllib.request + +MONIT_XML_URL = ("http://{host}:{port}/_status?format=xml" + .format(host=os.getenv("host", "localhost"), + port=os.getenv("port", "2812"))) def sanitize(s): @@ -51,53 +55,31 @@ def sanitize(s): return "".join([char for char in s if char in OK_CHARS]) -def get_monit_status_lines(): - # retrieve output +def get_monit_status_xml(): try: - output = subprocess.check_output(["monit", "status"]) - error_message = None - except (OSError, subprocess.CalledProcessError) as exc: - error_message = "Error running monit status command: %s" % str(exc) - if error_message is not None: - raise OSError(error_message) - # python3: the result of command execution is bytes and needs to be converted - if not isinstance(output, str): - output = output.decode("ascii", "ignore") - return output.splitlines() + conn = urllib.request.urlopen(MONIT_XML_URL) + except urllib.error.URLError as exc: + conn = None + if conn is None: + raise RuntimeError("Failed to open monit status URL: {}".format(MONIT_XML_URL)) + else: + return xml.dom.minidom.parse(conn) def parse_processes(): - cur_proc = None + dom = get_monit_status_xml() procs = {} - for line in get_monit_status_lines(): - m = re.match("^Process '(.*)'.*$", line) - if m: - cur_proc = sanitize(m.group(1)) - try: - procs[cur_proc] - except KeyError: - procs[cur_proc] = {} - continue - m = re.match(" memory kilobytes total\s+([0-9.]+)(.*)$", line) - if m: - size, suffix = m.groups() - # we store memory consumption as megabytes - factor = { - "": 1024 ** -1, - "KB": 1024 ** -1, - "MB": 1024 ** 0, - "GB": 1024 ** 1, - "TB": 1024 ** 2, - }[suffix.strip().upper()] - try: - procs[cur_proc]["total_memory"] = float(size) * factor - except ValueError: - procs[cur_proc]["total_memory"] = "U" - continue - m = re.match(" cpu percent total\s+([.0-9]+)%.*$", line) - if m: - procs[cur_proc]["total_cpu"] = m.group(1) - continue + for item in dom.getElementsByTagName("service"): + if item.getAttribute("type") == "3": + # daemon with memory usage and CPU + name = item.getElementsByTagName("name")[0].childNodes[0].data + memory_usage = item.getElementsByTagName("memory")[0].getElementsByTagName( + "kilobytetotal")[0].childNodes[0].data + cpu_usage = item.getElementsByTagName("cpu")[0].getElementsByTagName( + "percenttotal")[0].childNodes[0].data + procs[name] = {} + procs[name]["total_memory"] = memory_usage + procs[name]["total_cpu"] = cpu_usage return procs @@ -105,22 +87,22 @@ action = sys.argv[1] if (len(sys.argv) > 1) else None if action == 'autoconf': try: - get_monit_status_lines() + get_monit_status_xml() print("yes") - except OSError: + except RuntimeError: print("no (failed to request monit status)") elif action == 'config': procs = parse_processes() print('graph_title Per process stats from Monit') - print('graph_vlabel usage of memory [MB] or cpu [%]') + print('graph_vlabel usage of memory [kB] or cpu [%]') print('graph_category monit') for process in procs: for stat in procs[process]: - print("monit_%s_%s.label %s.%s" % (process, stat, process, stat)) + print("monit_%s_%s.label %s.%s" % (sanitize(process), stat, process, stat)) if stat == 'total_memory': # the allocated memory may never be zero - print("monit_%s_%s.warning 1:" % (process, stat)) + print("monit_%s_%s.warning 1:" % (sanitize(process), stat)) else: for process, stats in parse_processes().items(): for stat_key, stat_value in stats.items(): - print("monit_%s_%s.value %s" % (process, stat_key, stat_value)) + print("monit_%s_%s.value %s" % (sanitize(process), stat_key, stat_value)) From 8c9f0260f91a43f4d75a7f6d868e8d05d9f42b68 Mon Sep 17 00:00:00 2001 From: Wouter Verhelst Date: Sun, 23 Oct 2016 10:36:35 +0200 Subject: [PATCH 132/718] Use sh shebang Don't need bash-specific features, after all --- plugins/sge/sge_job_stats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sge/sge_job_stats b/plugins/sge/sge_job_stats index 075af312..7350eed0 100755 --- a/plugins/sge/sge_job_stats +++ b/plugins/sge/sge_job_stats @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # Graphs the number of running and waiting jobs, as well as when they # are submitted. From 5800201da8e1adfb9ff3b4f72e77bede4522eb8f Mon Sep 17 00:00:00 2001 From: Wouter Verhelst Date: Sun, 23 Oct 2016 10:36:58 +0200 Subject: [PATCH 133/718] Configurable location of sge settings.sh script --- plugins/sge/sge_job_stats | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/sge/sge_job_stats b/plugins/sge/sge_job_stats index 7350eed0..0423ca1e 100755 --- a/plugins/sge/sge_job_stats +++ b/plugins/sge/sge_job_stats @@ -24,7 +24,9 @@ then exit 0 fi -. /opt/sge/default/common/settings.sh +SGE_SETTINGS=${SGE_SETTINGS:-/opt/sge/default/common/settings.sh} + +. $SGE_SETTINGS qstat -u '*' | awk ' BEGIN{maxnum = 0; running = 0; waiting = 0} From c679de12ce1e273f970f36a3f36621f299f033a1 Mon Sep 17 00:00:00 2001 From: Jonas Palm Date: Mon, 24 Oct 2016 23:03:18 +0200 Subject: [PATCH 134/718] code rewrite removed bashisms and cleaned everything up --- plugins/other/wordpress-multisite | 167 ++++++++++++++---------------- 1 file changed, 80 insertions(+), 87 deletions(-) diff --git a/plugins/other/wordpress-multisite b/plugins/other/wordpress-multisite index 126d46b9..f5316f9c 100644 --- a/plugins/other/wordpress-multisite +++ b/plugins/other/wordpress-multisite @@ -1,11 +1,14 @@ -#!/bin/bash +#!/usr/bin/env sh # wordpress-multisite plugin # -# Author Jonas Palm -# E-Mail jonaspalm . posteo. de +# Version 0.2 +# Date 2016-10-24 +# Code improvements +# # Version 0.1 # Date 2016-02-07 -# +# Initial release +# : <<=cut =head1 NAME Wordpress-Multisite Munin Plugin @@ -21,108 +24,98 @@ The plugin need access to the wordpress database =head2 Config file Create the config file plugin-conf.d/wordpress with the following values: -=over 6 +=over 4 =item * [wordpress*] -=item * env.DB_USER -=item * env.DB_PASSWORD -=item * env.DB_NAME -=item * env.DB_PREFIX -=item * env.DB_HOST -=item * env.DB_PORT +=item * env.mysqlopts +=item * env.mysqlconnection +=item * env.database +=item * env.dbprefix =back -=head1 VERSION - -0.1 2016-02-07 +=head1 VERSION +Version 0.2 (2016-02-07) =head1 AUTHOR Jonas Palm +=head1 LICENSE + +GPLv3 or higher + =cut -# Fill some variables -DB_USER=${DB_USER} -DB_PASSWORD=${DB_PASSWORD} -DB_NAME=${DB_NAME:-wordpress} -DB_PREFIX=${DB_PREFIX:-wp_} -DB_HOST=${DB_HOST:-localhost} -DB_PORT=${DB_PORT:-3306} +# fill vars +DB_OPTIONS=${mysqlopts} +DB_CONNECTION=${mysqlconnection:--hlocalhost} +DB_NAME=${database} +DB_PREFIX=${dbprefix:-wp_} -MYSQLOPTS="-h$DB_HOST -P $DB_PORT -p$DB_PASSWORD -u$DB_USER -D $DB_NAME --column-names=0 -s" +MYSQL_CMD=$(which mysql) +wp_get() { + case $1 in + comments) QUERY="SELECT COUNT(*) FROM ${DB_PREFIX}${2}comments WHERE comment_approved = '1' AND comment_type = '';" ;; + ids) QUERY="SELECT blog_id FROM ${DB_PREFIX}blogs;" ;; + pingbacks) QUERY="SELECT COUNT(*) FROM ${DB_PREFIX}${2}comments WHERE comment_approved = '1' AND comment_type = 'pingback';" ;; + posts) QUERY="SELECT COUNT(*) FROM ${DB_PREFIX}${2}posts WHERE post_status = 'publish' AND post_password = '' AND post_type = 'post';" ;; + title) QUERY="SELECT option_value FROM ${DB_PREFIX}${2}options WHERE option_name = 'siteurl';" ;; + users) QUERY="SELECT COUNT(*) FROM ${DB_PREFIX}users;" + esac + $MYSQL_CMD $DB_CONNECTION $DB_OPTIONS $DB_NAME --column-names=0 -s --execute="$QUERY" +} + +# whole network if [ "$1" = "config" ]; then - echo "multigraph wordpress" - echo "graph_title Wordpress Mulitsite" - echo "graph_order instances users posts comments pingbacks" - echo "graph_vlabel Wordpress" - echo "graph_category Wordpress" - echo "graph_info Some Statistics of Wordpress" - echo "instances.label Instances" - echo "users.label Users" - echo "posts.label Posts" - echo "comments.label Comments" - echo "pingbacks.label Pingbacks" + echo "multigraph wordpress" + echo "graph_title Wordpress Mulitsite" + echo "graph_order instances users posts comments pingbacks" + echo "graph_vlabel Wordpress" + echo "graph_category Wordpress" + echo "graph_info Some Statistics of Wordpress" + echo "instances.label Instances" + echo "users.label Users" + echo "posts.label Posts" + echo "comments.label Comments" + echo "pingbacks.label Pingbacks" else - CNT=0 - for n in `mysql $MYSQLOPTS --execute="select blog_id from ${DB_PREFIX}blogs"`; do - if [ "$n" == "1" ]; then - i= - else - i=${n}_ - fi + for n in $(wp_get ids); do + i= + test "$n" -gt "1" && i=${n}_ - POSTS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}posts WHERE post_status = 'publish' AND post_password = '' AND post_type = 'post';"` - (( POSTS_ += POSTS )) + POSTS=$(expr $POSTS + $(wp_get posts $i)) + COMMENTS=$(expr $COMMENTS + $(wp_get comments $i)) + PINGBACKS=$(expr $PINGBACKS + $(wp_get pingbacks $i)) + CNT=$(expr $CNT + 1) + done - COMMENTS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}comments WHERE comment_approved = '1' AND comment_type = '';"` - (( COMMENTS_ += COMMENTS )) - - PINGBACKS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}comments WHERE comment_approved = '1' AND comment_type = 'pingback';"` - (( PINGBACKS_ += PINGBACKS )) - - (( CNT += 1 )) - done - - # return values - echo "multigraph wordpress" - echo "posts.value $POSTS_" - echo "comments.value $COMMENTS_" - echo "pingbacks.value $PINGBACKS_" - echo "instances.value $CNT" - echo "users.value `mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}users ;"`" + echo "multigraph wordpress" + echo "posts.value $POSTS" + echo "comments.value $COMMENTS" + echo "pingbacks.value $PINGBACKS" + echo "instances.value $CNT" + echo "users.value $(wp_get users)" fi # single blogs -for n in `mysql $MYSQLOPTS --execute="select blog_id from ${DB_PREFIX}blogs"`; do - if [ "${n}" == "1" ]; then - i= - else - i=${n}_ - fi +for n in $(wp_get ids); do + i= + test "$n" -gt "1" && i=${n}_ + test "$n" -le "9" && n=0${n} - if [ "$n" -le "9" ]; then - n=0${n} - fi - - if [ "$1" = "config" ]; then - echo "multigraph wordpress.site_${n}" - echo "graph_title `mysql $MYSQLOPTS --execute=\"select option_value from ${DB_PREFIX}${i}options where option_name = 'siteurl';\"`" - echo "graph_order posts comments pingbacks" - echo "graph_vlabel Wordpress ID ${n}" - echo "posts.label Posts" - echo "comments.label Comments" - echo "pingbacks.label Pingbacks" - else - POSTS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}posts WHERE post_status = 'publish' AND post_password = '' AND post_type = 'post';"` - COMMENTS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}comments WHERE comment_approved = '1' AND comment_type = '';"` - PINGBACKS=`mysql $MYSQLOPTS --execute="SELECT COUNT(*) FROM ${DB_PREFIX}${i}comments WHERE comment_approved = '1' AND comment_type = 'pingback';"` - - # return values - echo "multigraph wordpress.site_${n}" - echo "posts.value $POSTS" - echo "comments.value $COMMENTS" - echo "pingbacks.value $PINGBACKS" - fi + if [ "$1" = "config" ]; then + echo "multigraph wordpress.site_${n}" + echo "graph_title $(wp_get title $i)" + echo "graph_order posts comments pingbacks" + echo "graph_vlabel Wordpress ID ${n}" + echo "posts.label Posts" + echo "comments.label Comments" + echo "pingbacks.label Pingbacks" + else + echo "multigraph wordpress.site_${n}" + echo "posts.value $(wp_get posts $i)" + echo "comments.value $(wp_get comments $i)" + echo "pingbacks.value $(wp_get pingbacks $i)" + fi done From ef80db4bc1a4967722bdb34ebfb9fd34b33bdee3 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Tue, 25 Oct 2016 21:21:24 +1100 Subject: [PATCH 135/718] [systemd] Remove half-baked suggest Signed-off-by: Olivier Mehani --- plugins/system/systemd | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/plugins/system/systemd b/plugins/system/systemd index 40f388d5..cb0f183d 100755 --- a/plugins/system/systemd +++ b/plugins/system/systemd @@ -17,7 +17,7 @@ None needed. =head1 AUTHOR -Olivier Mehani +Olivier Mehani =head1 LICENSE @@ -26,7 +26,7 @@ GPLv2 =head1 MAGIC MARKERS #%# family=auto - #%# capabilities=autoconf suggest + #%# capabilities=autoconf =cut @@ -41,33 +41,22 @@ autoconf() { systemctl --state=failed --no-pager --no-legend >/dev/null 2>&1 && echo yes || echo "no (No systemctl or error running it)" } -suggest() { - echo "units" -} - config () { - case $1 in - "units") - cat << EOF + cat << EOF graph_title Systemd units state graph_args -l 0 graph_category system graph_scale no graph_vlabel units EOF -for state in $states; do - echo "$state.label $state" - echo "$state.draw AREASTACK" - if [ $state = failed ]; then - echo "$state.warning 0" - echo "$state.critical 10" - fi -done - ;; - "*") - echo "$0: unknown mode '$1'" >&2 - exit 1 - esac + for state in $states; do + echo "$state.label $state" + echo "$state.draw AREASTACK" + if [ $state = failed ]; then + echo "$state.warning 0" + echo "$state.critical 10" + fi + done } fetch () { @@ -99,9 +88,6 @@ case $1 in "autoconf") autoconf ;; - "suggest") - suggest - ;; "config") config $mode ;; From 20de5307cb43aa65629788e9348a3867257ad727 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Tue, 25 Oct 2016 23:01:58 +1100 Subject: [PATCH 136/718] [upnpc_] Move to network/ and document graphs Signed-off-by: Olivier Mehani --- plugins/{upnpc => network}/upnpc_ | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) rename plugins/{upnpc => network}/upnpc_ (85%) diff --git a/plugins/upnpc/upnpc_ b/plugins/network/upnpc_ similarity index 85% rename from plugins/upnpc/upnpc_ rename to plugins/network/upnpc_ index d45a7ebf..e3c933c5 100755 --- a/plugins/upnpc/upnpc_ +++ b/plugins/network/upnpc_ @@ -7,6 +7,13 @@ upnpc_ - Plugin to monitor routers via UPnP +This plugin uses the upnpc utility (package miniupnpc in Debian), to monitor an +router using UPnP. It can monitor the following aspects, and plot them as separate graphs: +* uptime: how long the link has been up; +* bitrate: the up and downlink bitrate (e.g., sync speed for DSL); +* traffic: the actual up and downstream traffic rate; +* pkts: the number of packets coming in and out. + =head1 APPLICABLE SYSTEMS Linux systems with upnpc installed. @@ -17,7 +24,7 @@ None needed. =head1 AUTHOR -Olivier Mehani +Olivier Mehani =head1 LICENSE From 5884bddab198296ddd1d42a0d6b3953a6dabcee7 Mon Sep 17 00:00:00 2001 From: samuraiii Date: Tue, 25 Oct 2016 22:39:56 +0200 Subject: [PATCH 137/718] To get rid of non ascii characters in graphs --- plugins/network/transmission_ratios/tr_ratios | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/network/transmission_ratios/tr_ratios b/plugins/network/transmission_ratios/tr_ratios index ec2daae9..343b1b46 100755 --- a/plugins/network/transmission_ratios/tr_ratios +++ b/plugins/network/transmission_ratios/tr_ratios @@ -8,7 +8,7 @@ if [ "$1" = "config" ]; then echo "graph_vlabel Seed ratio %" echo "graph_category torrent" echo "graph_info This plugin shows your transmission ratios per torrent" - transmission-remote -n $user:$pass -l | gawk -f /usr/share/munin/plugins/tr_ratios_labels + transmission-remote -n $user:$pass -l | gawk -f /usr/share/munin/plugins/tr_ratios_labels | iconv -f utf-8 -t ascii//translit exit 0 fi From 8777efd66c60b6122a5e87fc3aa2cdb1622d1139 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 26 Oct 2016 00:34:47 +0200 Subject: [PATCH 138/718] [tr_ratios] switch from bash to sh; quoting --- plugins/network/transmission_ratios/tr_ratios | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/network/transmission_ratios/tr_ratios b/plugins/network/transmission_ratios/tr_ratios index 343b1b46..316b6088 100755 --- a/plugins/network/transmission_ratios/tr_ratios +++ b/plugins/network/transmission_ratios/tr_ratios @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh user='' pass='' @@ -8,9 +8,8 @@ if [ "$1" = "config" ]; then echo "graph_vlabel Seed ratio %" echo "graph_category torrent" echo "graph_info This plugin shows your transmission ratios per torrent" - transmission-remote -n $user:$pass -l | gawk -f /usr/share/munin/plugins/tr_ratios_labels | iconv -f utf-8 -t ascii//translit + transmission-remote -n "$user:$pass" -l | gawk -f /usr/share/munin/plugins/tr_ratios_labels | iconv -f utf-8 -t ascii//translit exit 0 fi -transmission-remote -n $user:$pass -l | gawk -f /usr/share/munin/plugins/tr_ratios_data - +transmission-remote -n "$user:$pass" -l | gawk -f /usr/share/munin/plugins/tr_ratios_data From 1a25481e2c52769215669fb874eb0e57f17a3f90 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 26 Oct 2016 00:38:07 +0200 Subject: [PATCH 139/718] [tr_ratios] include awk scripts; awk instead of gawk since we cannot rely on the location of the two included awk scripts, we just include them literally --- plugins/network/transmission_ratios/tr_ratios | 20 +++++++++++++++++-- .../transmission_ratios/tr_ratios_data | 10 ---------- .../transmission_ratios/tr_ratios_labels | 8 -------- 3 files changed, 18 insertions(+), 20 deletions(-) delete mode 100644 plugins/network/transmission_ratios/tr_ratios_data delete mode 100644 plugins/network/transmission_ratios/tr_ratios_labels diff --git a/plugins/network/transmission_ratios/tr_ratios b/plugins/network/transmission_ratios/tr_ratios index 316b6088..ce058a68 100755 --- a/plugins/network/transmission_ratios/tr_ratios +++ b/plugins/network/transmission_ratios/tr_ratios @@ -8,8 +8,24 @@ if [ "$1" = "config" ]; then echo "graph_vlabel Seed ratio %" echo "graph_category torrent" echo "graph_info This plugin shows your transmission ratios per torrent" - transmission-remote -n "$user:$pass" -l | gawk -f /usr/share/munin/plugins/tr_ratios_labels | iconv -f utf-8 -t ascii//translit + transmission-remote -n "$user:$pass" -l | awk ' + BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } + NR > 1 { + split($1,torrentid," ") + if (torrentid[1] != "Sum:") { + print "ID" torrentid[1] ".label " $9 + } + }' | iconv -f utf-8 -t ascii//translit exit 0 fi -transmission-remote -n "$user:$pass" -l | gawk -f /usr/share/munin/plugins/tr_ratios_data +transmission-remote -n "$user:$pass" -l | awk ' + BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } + NR > 1 { + split($1,torrentid," ") + if (torrentid[1] != "Sum:") { + split($7,ratio," ") + ratio[1] = ratio[1] * 100 + print "ID" torrentid[1] ".value " ratio[1] + } + }' diff --git a/plugins/network/transmission_ratios/tr_ratios_data b/plugins/network/transmission_ratios/tr_ratios_data deleted file mode 100644 index fd9dd551..00000000 --- a/plugins/network/transmission_ratios/tr_ratios_data +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } -NR > 1 { - split($1,torrentid," ") - if(torrentid[1] != "Sum:") { - split($7,ratio," ") - ratio[1] = ratio[1] * 100 - print "ID" torrentid[1] ".value " ratio[1] - } -} - diff --git a/plugins/network/transmission_ratios/tr_ratios_labels b/plugins/network/transmission_ratios/tr_ratios_labels deleted file mode 100644 index 273ab3d3..00000000 --- a/plugins/network/transmission_ratios/tr_ratios_labels +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } -NR > 1 { - split($1,torrentid," ") - if(torrentid[1] != "Sum:") { - print "ID" torrentid[1] ".label " $9 - } -} - From 839d825afed3dfab08a3ab2692b491290e9651ec Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 26 Oct 2016 02:04:11 +0200 Subject: [PATCH 140/718] [tr_ratios] add documentation --- plugins/network/transmission_ratios/tr_ratios | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/plugins/network/transmission_ratios/tr_ratios b/plugins/network/transmission_ratios/tr_ratios index ce058a68..1e59ff3f 100755 --- a/plugins/network/transmission_ratios/tr_ratios +++ b/plugins/network/transmission_ratios/tr_ratios @@ -1,7 +1,43 @@ #!/bin/sh +# -*- sh -*- user='' pass='' +: <<=cut + +=head1 NAME + +tr_ratios - monitor transfer ratios of the "transmission" bittorent program + +=head1 APPLICABLE SYSTEMS + +Any system with "transmission" installed and a transmission daemon running. + +=head1 CONFIGURATION + +Maybe you need to configure access credentials and connection settings: + + [tr_ratios] + env.host localhost + env.port 9091 + env.username alice + env.password secret + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 AUTHOR + +unspecified + +=head1 LICENSE + +unspecified + +=cut + if [ "$1" = "config" ]; then echo "graph_title Transmission seed ratios" From c3660c2ac029b900e7ad0d0cf44cef9fb65b325b Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Wed, 26 Oct 2016 02:23:06 +0200 Subject: [PATCH 141/718] [tr_ratios] restructure code * allow configuration of username, passwort, host and port via env * reduce code duplication * remove "*" from stopped transmissions --- plugins/network/transmission_ratios/tr_ratios | 64 +++++++++++++------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/plugins/network/transmission_ratios/tr_ratios b/plugins/network/transmission_ratios/tr_ratios index 1e59ff3f..b2be2160 100755 --- a/plugins/network/transmission_ratios/tr_ratios +++ b/plugins/network/transmission_ratios/tr_ratios @@ -1,8 +1,6 @@ #!/bin/sh # -*- sh -*- -user='' -pass='' : <<=cut =head1 NAME @@ -39,29 +37,55 @@ unspecified =cut +CONNECTION_ARG="${host:-localhost}:${port:-9091}" +USERNAME="${username:-}" +PASSWORD="${password:-}" + + +# return a space separated list of transmissions with the following columns: +# * fieldname +# * ratio (in percent) +# * name of the transmissions +request_transmission_stats() { + if [ -n "$USERNAME$PASSWORD" ]; then + transmission-remote "$CONNECTION_ARG" --auth "$USERNAME:$PASSWORD" --list + else + transmission-remote "$CONNECTION_ARG" --list + fi | awk ' + BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } + NR > 1 { + split($1,torrentid," ") + # remove "*" from the ID of stopped transmissions + sub(/\*/, "", torrentid[1]) + if (torrentid[1] != "Sum:") { + split($7,ratio," ") + ratio[1] = ratio[1] * 100 + print "ID" torrentid[1], ratio[1], $9 + } + }' +} + + +if [ "$1" = "autoconf" ]; then + if [ -n "$(request_transmission_stats 2>/dev/null)" ]; then + echo "yes" + else + if which transmission-remote >/dev/null; then + echo "no (failed to connect to daemon)" + else + echo "no (missing 'transmission-remote' program)" + fi + fi + exit 0 +fi + if [ "$1" = "config" ]; then echo "graph_title Transmission seed ratios" echo "graph_vlabel Seed ratio %" echo "graph_category torrent" echo "graph_info This plugin shows your transmission ratios per torrent" - transmission-remote -n "$user:$pass" -l | awk ' - BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } - NR > 1 { - split($1,torrentid," ") - if (torrentid[1] != "Sum:") { - print "ID" torrentid[1] ".label " $9 - } - }' | iconv -f utf-8 -t ascii//translit + request_transmission_stats | awk '{print $1 ".label " $3 }' | iconv -f utf-8 -t ascii//translit exit 0 fi -transmission-remote -n "$user:$pass" -l | awk ' - BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } - NR > 1 { - split($1,torrentid," ") - if (torrentid[1] != "Sum:") { - split($7,ratio," ") - ratio[1] = ratio[1] * 100 - print "ID" torrentid[1] ".value " ratio[1] - } - }' +request_transmission_stats | awk '{print $1 ".value " $2 }' From 31bedc08221fedb380f84f07da887d492054c843 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Wed, 26 Oct 2016 14:08:34 -0700 Subject: [PATCH 142/718] Hardcode tc path so we can run the script as non-root --- plugins/network/tc_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 7b29c7f7..cacfbf1b 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -16,7 +16,7 @@ DEVICE=${0##*/tc_} mytc() { - tc -s class show dev $1 | tr "\n" "|" | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n + /sbin/tc -s class show dev $1 | tr "\n" "|" | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n } case $1 in From afe87e904e7a78d7a994969ece91c2c8c02bd7b1 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Wed, 26 Oct 2016 14:10:46 -0700 Subject: [PATCH 143/718] Cosmetic whitespace uniformity --- plugins/network/tc_ | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index cacfbf1b..0d21c6cf 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -37,16 +37,15 @@ case $1 in gsub(/^ +/,"",a[1]); if (($2 > 0) || ($10 > 0)) print a[1]; }' /proc/net/dev -# egrep '^ *(eth|tap|bond|wlan|ath|ra|sw)[0-9]+:' /proc/net/dev | cut -f1 -d: | sed 's/ //g' +# egrep '^ *(eth|tap|bond|wlan|ath|ra|sw)[0-9]+:' /proc/net/dev | cut -f1 -d: | sed 's/ //g' fi exit 0 ;; config) - echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 }' | tr "\n" " "`" echo "graph_title $DEVICE TC traffic" - echo 'graph_args --base 1000' - echo 'graph_vlabel bits per ${graph_period}' + echo 'graph_args --base 1000' + echo 'graph_vlabel bits per ${graph_period}' echo 'graph_category network' echo "graph_info This graph shows the TC classes traffic of the $DEVICE network interface. Please note that the traffic is shown in bits per second, not bytes." From 234a753fea82ce16827a2f85af10f3d990ae3f08 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Wed, 26 Oct 2016 14:12:00 -0700 Subject: [PATCH 144/718] Make awk script more readable --- plugins/network/tc_ | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 0d21c6cf..c912c89d 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -48,8 +48,21 @@ case $1 in echo 'graph_vlabel bits per ${graph_period}' echo 'graph_category network' echo "graph_info This graph shows the TC classes traffic of the $DEVICE network interface. Please note that the traffic is shown in bits per second, not bytes." - - mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 ".type DERIVE\n" $2 "_" $3 "_" $4 ".min 0\n" $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*" }' + + # the root(s) + mytc $DEVICE | grep -v " parent " | tr "_" " " | awk '{ + print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4; + print $2 "_" $3 "_" $4 ".type DERIVE"; + print $2 "_" $3 "_" $4 ".min 0"; + print $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*"; + }' + # the child(s) + mytc $DEVICE | grep " parent " | tr "_" " " | awk '{ + print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4; + print $2 "_" $3 "_" $4 ".type DERIVE"; + print $2 "_" $3 "_" $4 ".min 0"; + print $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*"; + }' exit 0 ;; esac From 3dd825dd62601a15bb9d685def8c5dd9653728d8 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Wed, 26 Oct 2016 14:14:26 -0700 Subject: [PATCH 145/718] Output fields of tc can vary, possibly causing corrupted data to be gathered. Look specifically for the "bytes" keyword --- plugins/network/tc_ | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index c912c89d..77000c4e 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -68,9 +68,15 @@ case $1 in esac # the root(s) -mytc $DEVICE | grep -v " parent " | awk "{ print \$2 \"_\" \$3 \".value \" \$14}" +mytc $DEVICE | grep -v " parent " | awk '{ + split(substr($0, match($0, /[0-9]+ [Bb]ytes/)), a, " "); + print $2 "_" $3 ".value " a[1]; +}' # the child(s) -mytc $DEVICE | grep " parent " | awk "{ print \$2 \"_\" \$3 \".value \" \$19}" +mytc $DEVICE | grep " parent " | awk '{ + split(substr($0, match($0, /[0-9]+ [Bb]ytes/)), a, " "); + print $2 "_" $3 ".value " a[1]; +}' exit 0 From 13f9ba7a6159b940b396d157eb2c372dd35cc980 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Wed, 26 Oct 2016 14:15:48 -0700 Subject: [PATCH 146/718] Area stack children --- plugins/network/tc_ | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 77000c4e..a870bf3c 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -56,12 +56,14 @@ case $1 in print $2 "_" $3 "_" $4 ".min 0"; print $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*"; }' + # TODO: only AREASTACK things with no children # the child(s) mytc $DEVICE | grep " parent " | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4; print $2 "_" $3 "_" $4 ".type DERIVE"; print $2 "_" $3 "_" $4 ".min 0"; print $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*"; + print $2 "_" $3 "_" $4 ".draw AREASTACK"; }' exit 0 ;; From b31b861f1c2917945bb77c3495a4971cb4e2f7f5 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Thu, 27 Oct 2016 22:25:27 +1100 Subject: [PATCH 147/718] [systemd_units] Rename, remove unused mode logic and cleanup syntax Signed-off-by: Olivier Mehani --- plugins/system/{systemd => systemd_units} | 40 +++++++++-------------- 1 file changed, 15 insertions(+), 25 deletions(-) rename plugins/system/{systemd => systemd_units} (64%) diff --git a/plugins/system/systemd b/plugins/system/systemd_units similarity index 64% rename from plugins/system/systemd rename to plugins/system/systemd_units index cb0f183d..4f9ec61c 100755 --- a/plugins/system/systemd +++ b/plugins/system/systemd_units @@ -52,7 +52,7 @@ EOF for state in $states; do echo "$state.label $state" echo "$state.draw AREASTACK" - if [ $state = failed ]; then + if [ "$state" = "failed" ]; then echo "$state.warning 0" echo "$state.critical 10" fi @@ -60,38 +60,28 @@ EOF } fetch () { - case $1 in - "units") - tmp=`mktemp -t munin-systemd.XXXXXX` - systemctl --no-pager --no-legend --all | awk '{print $1, $3}' > $tmp - for state in \ - $states ; do - echo -n "$state.value " - grep $state$ $tmp | wc -l - extinfo=`grep $state$ $tmp | cut -d " " -f 1` - if [ "$extinfo" ]; then - echo "$state.extinfo" $extinfo - fi - done - rm $tmp - ;; - "*") - echo "$0: unknown mode '$1'" >&2 - exit 1 - esac + tmp=$(mktemp -t munin-systemd_units.XXXXXX) + trap "rm \"$tmp\"" EXIT + systemctl --no-pager --no-legend --all | awk '{print $1, $3}' > "$tmp" + for state in \ + $states ; do + echo -n "$state.value " + grep -c "$state$" "$tmp" + extinfo=$(grep "$state$" "$tmp" | cut -d " " -f 1) + if [ -n "$extinfo" ]; then + echo "$state.extinfo" $extinfo + fi +done } -# mode=`echo $0 | sed 's/.*_//'` -mode=units - case $1 in "autoconf") autoconf ;; "config") - config $mode + config ;; *) - fetch $mode + fetch ;; esac From a2f1592fe5a1dd286d2ea31f9842c25ac3b280fc Mon Sep 17 00:00:00 2001 From: Tomaz Solc Date: Thu, 27 Oct 2016 17:49:03 +0200 Subject: [PATCH 148/718] systemd_units: avoid use of temporary file --- plugins/system/systemd_units | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/system/systemd_units b/plugins/system/systemd_units index 4f9ec61c..34563fd3 100755 --- a/plugins/system/systemd_units +++ b/plugins/system/systemd_units @@ -60,14 +60,12 @@ EOF } fetch () { - tmp=$(mktemp -t munin-systemd_units.XXXXXX) - trap "rm \"$tmp\"" EXIT - systemctl --no-pager --no-legend --all | awk '{print $1, $3}' > "$tmp" + tmp=$(systemctl --no-pager --no-legend --all | awk '{print $1, $3}') for state in \ $states ; do echo -n "$state.value " - grep -c "$state$" "$tmp" - extinfo=$(grep "$state$" "$tmp" | cut -d " " -f 1) + echo "$tmp" | grep -c "$state$" + extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1) if [ -n "$extinfo" ]; then echo "$state.extinfo" $extinfo fi From a9476f50fdbe656f601c9616112f2ea3f8b5d7a7 Mon Sep 17 00:00:00 2001 From: Tomaz Solc Date: Thu, 27 Oct 2016 17:51:33 +0200 Subject: [PATCH 149/718] systemd_units: fix indent --- plugins/system/systemd_units | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/system/systemd_units b/plugins/system/systemd_units index 34563fd3..dd336dd7 100755 --- a/plugins/system/systemd_units +++ b/plugins/system/systemd_units @@ -61,15 +61,14 @@ EOF fetch () { tmp=$(systemctl --no-pager --no-legend --all | awk '{print $1, $3}') - for state in \ - $states ; do - echo -n "$state.value " - echo "$tmp" | grep -c "$state$" - extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1) - if [ -n "$extinfo" ]; then - echo "$state.extinfo" $extinfo - fi -done + for state in $states ; do + echo -n "$state.value " + echo "$tmp" | grep -c "$state$" + extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1) + if [ -n "$extinfo" ]; then + echo "$state.extinfo" $extinfo + fi + done } case $1 in From adc00722860487e663915617caeb60c4388e061d Mon Sep 17 00:00:00 2001 From: Tomaz Solc Date: Thu, 27 Oct 2016 17:52:30 +0200 Subject: [PATCH 150/718] systemd_units: avoid use of echo -n flag --- plugins/system/systemd_units | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/system/systemd_units b/plugins/system/systemd_units index dd336dd7..0a55adc5 100755 --- a/plugins/system/systemd_units +++ b/plugins/system/systemd_units @@ -62,8 +62,8 @@ EOF fetch () { tmp=$(systemctl --no-pager --no-legend --all | awk '{print $1, $3}') for state in $states ; do - echo -n "$state.value " - echo "$tmp" | grep -c "$state$" + count=$(echo "$tmp" | grep -c "$state$") + echo "$state.value $count" extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1) if [ -n "$extinfo" ]; then echo "$state.extinfo" $extinfo From 38835f7a62111ebdbc3ea91a47ee2174e7d225f6 Mon Sep 17 00:00:00 2001 From: Tomaz Solc Date: Thu, 27 Oct 2016 17:54:01 +0200 Subject: [PATCH 151/718] systemd_units: add doublequote around $extinfo --- plugins/system/systemd_units | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/system/systemd_units b/plugins/system/systemd_units index 0a55adc5..e7f3960f 100755 --- a/plugins/system/systemd_units +++ b/plugins/system/systemd_units @@ -64,9 +64,9 @@ fetch () { for state in $states ; do count=$(echo "$tmp" | grep -c "$state$") echo "$state.value $count" - extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1) + extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1 | tr -d '\n') if [ -n "$extinfo" ]; then - echo "$state.extinfo" $extinfo + echo "$state.extinfo" "$extinfo" fi done } From d888c31d1d9ce2a997bc43e0d90baad31e6a0c7c Mon Sep 17 00:00:00 2001 From: Tomaz Solc Date: Thu, 27 Oct 2016 18:19:16 +0200 Subject: [PATCH 152/718] systemd_units: fix use of tr --- plugins/system/systemd_units | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/system/systemd_units b/plugins/system/systemd_units index e7f3960f..33e59115 100755 --- a/plugins/system/systemd_units +++ b/plugins/system/systemd_units @@ -64,7 +64,7 @@ fetch () { for state in $states ; do count=$(echo "$tmp" | grep -c "$state$") echo "$state.value $count" - extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1 | tr -d '\n') + extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1 | tr '\n' ' ') if [ -n "$extinfo" ]; then echo "$state.extinfo" "$extinfo" fi From 50c13da584f1ab77c1727ff029734e5a6af8b0df Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Thu, 4 Aug 2016 12:09:06 +0200 Subject: [PATCH 153/718] [udp-statistics] rename 'errors' field, add more fields --- plugins/network/udp-statistics | 47 ++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/plugins/network/udp-statistics b/plugins/network/udp-statistics index 5a3e78a8..aad671e2 100755 --- a/plugins/network/udp-statistics +++ b/plugins/network/udp-statistics @@ -16,25 +16,50 @@ graph_title UDP Statistics graph_args --base 1000 -l 0 graph_vlabel Packets/\${graph_period} graph_category network -received.label Received -received.draw AREA -received.type DERIVE -received.min 0 -errors.label Errors -errors.draw LINE1 -errors.type DERIVE -errors.min 0 sent.label Sent sent.draw LINE1 sent.type DERIVE sent.min 0 +received.label Received +received.draw AREA +received.type DERIVE +received.min 0 +unknown_ports.label In Unknown Port +unknown_ports.draw LINE1 +unknown_ports.type DERIVE +unknown_ports.min 0 +receive_buffer_errors.label Receive Buffer Errors +receive_buffer_errors.draw LINE1 +receive_buffer_errors.type DERIVE +receive_buffer_errors.min 0 +send_buffer_errors.label Send Buffer Errors +send_buffer_errors.draw LINE1 +send_buffer_errors.type DERIVE +send_buffer_errors.min 0 +in_csum_errors.label In CSUM Errors +in_csum_errors.draw LINE1 +in_csum_errors.type DERIVE +in_csum_errors.min 0 +ignored_multis.label In Ignored Multis +ignored_multis.draw LINE1 +ignored_multis.type DERIVE +ignored_multis.min 0 +receive_errors.label Receive Errors +receive_errors.draw LINE1 +receive_errors.type DERIVE +receive_errors.min 0 EOM exit 0; } } -@netstat = qx{/bin/netstat -us | awk '/packets sent/ \{ print "sent.value " \$1 \} +@netstat = qx{/bin/netstat -us | grep -A8 '^Udp:' | awk '/packets sent/ \{ print "sent.value " \$1 \} /packets received/ \{ print "received.value " \$1 \} - /packet receive errors/ \{ print "errors.value " \$1 \}'}; + /packets to unknown port received/ \{ print "unknown_ports.value " \$1 \} + /receive buffer errors/ \{ print "receive_buffer_errors.value " \$1 \} + /send buffer errors/ \{ print "send_buffer_errors.value " \$1 \} + /InCsumErrors/ \{ print "in_csum_errors.value " \$2 \} + /IgnoredMulti/ \{ print "ignored_multis.value " \$2 \} + /packet receive errors/ \{ print "receive_errors.value " \$1 \}'}; -print @netstat; \ No newline at end of file +print @netstat; From f751a0d865bf5b99e95eb18692cd114d7208091d Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sat, 29 Oct 2016 11:50:50 +1100 Subject: [PATCH 154/718] [fresh-backups] Rename, cleanup, and add POD doc Signed-off-by: Olivier Mehani --- plugins/backup/backup-manager | 39 ------------------ plugins/backup/fresh-backups | 78 +++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 39 deletions(-) delete mode 100755 plugins/backup/backup-manager create mode 100755 plugins/backup/fresh-backups diff --git a/plugins/backup/backup-manager b/plugins/backup/backup-manager deleted file mode 100755 index d2eabd00..00000000 --- a/plugins/backup/backup-manager +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh - -# Example config: -# [backup-manager] -# user root -# env.backup_dir /path/to/your/backups/ -# env.lifetime 2 -# env.archive_pattern *.tar.bz2 -# env.backup_number 4 - -# Configuration directives, edit before first use. -BACKUP_DIR=${backup_dir:-/data/backup} -ARCHIVE_PATTERN="${archive_pattern:-*.tar.bz2}" -# How old backups should be considered as non-yound anymore in [days]. -LIFETIME=${lifetime:-2} -# Critical states will be issued when the number of fresh backups archives is below `backup_number`, -# and warnings below `backup_number*lifetime` -CRIT=${backup_number:-1} -WARN=$((${CRIT}*${LIFETIME})) - -# The situation is critical if there are no young files, the backup is down. -case $1 in - config) - cat << EOF -graph_title Fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} -graph_vlabel number -graph_args -l 0 -graph_category system -freshcount.label number -freshcount.critical ${CRIT}: -freshcount.warning ${WARN}: -EOF - exit 0;; -esac - -printf "freshcount.value " -find $BACKUP_DIR -name "${ARCHIVE_PATTERN}" -a -mtime -$LIFETIME | wc -l -printf "freshcount.extinfo " -du -sh $BACKUP_DIR diff --git a/plugins/backup/fresh-backups b/plugins/backup/fresh-backups new file mode 100755 index 00000000..ce46ae7c --- /dev/null +++ b/plugins/backup/fresh-backups @@ -0,0 +1,78 @@ +#!/bin/sh + +: << =cut + +=head1 NAME + +fresh-backups - Plugin to monitor the freshness of backup files + +=head1 APPLICABLE SYSTEMS + +Any system with some automated backup creating or updating archive files. + +This works well with backup-manager. + +=head1 CONFIGURATION + +The following example checks all tar.bz2 files in /path/to/your/backups/, and +counts all those that are less than 2 days old, and there should be 4 separate +daily archives. + + [fresh-backups] + user root + env.backup_dir /path/to/your/backups/ + env.lifetime 2 + env.archive_pattern *.tar.bz2 + env.backup_number 4 + +This will also set the warning and critical values for this plugin to 2*4 and +4, respectively, meaning that if the number of fresh files goes below those +limits, the relevant notifications will be triggerred. + + export BM_REPOSITORY_ROOT="/path/to/your/backups" + export BM_TARBALL_FILETYPE="tar.bz2" + export BM_TARBALL_DIRECTORIES="/etc /home /srv /data" + +=head1 AUTHOR + +Olivier Mehani + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut + +# Configuration directives, edit before first use. +BACKUP_DIR=${backup_dir:-/data/backup} +ARCHIVE_PATTERN="${archive_pattern:-*.tar.bz2}" +# How old backups should be considered as non-yound anymore in [days]. +LIFETIME=${lifetime:-2} +# Critical states will be issued when the number of fresh backups archives is below `backup_number`, +# and warnings below `backup_number*lifetime` +CRIT=${backup_number:-1} +WARN=$((CRIT*LIFETIME)) + +# The situation is critical if there are no young files, the backup is down. +case $1 in + config) + cat << EOF +graph_title Fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} +graph_vlabel number +graph_args -l 0 +graph_category system +freshcount.label number +freshcount.critical ${CRIT}: +freshcount.warning ${WARN}: +EOF + exit 0;; +esac + +printf "freshcount.value " +find "${BACKUP_DIR}" -name "${ARCHIVE_PATTERN}" -a -mtime "-${LIFETIME}" | wc -l +printf "freshcount.extinfo " +du -sh "${BACKUP_DIR}" From 5cfc73c3c8b5efede38bb60710caa3b2bfe3beaa Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sat, 29 Oct 2016 16:08:19 +1100 Subject: [PATCH 155/718] [deborphan] Cleanup and add POD doc Signed-off-by: Olivier Mehani --- plugins/system/deborphan | 71 ++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/plugins/system/deborphan b/plugins/system/deborphan index 19eafee2..e063de53 100755 --- a/plugins/system/deborphan +++ b/plugins/system/deborphan @@ -1,36 +1,57 @@ #!/bin/bash -# -# Plugin to monitor the number of orphaned packages on the -# system (using deborphan). Might work on section distib, who knows... -# -# Based on the debsecan plugin by Nicolas Bouthors. 2016-09-02 -# -# Olivie Mehani -# -# Licence : GPLv2 -# -#%# family=auto -#%# capabilities=autoconf + +: << =cut + +=head1 NAME + +deborphan - Monitor orphaned Debian packages + +=head1 APPLICABLE SYSTEMS + +Debian-ish systems with deborphan installed. + +=head1 CONFIGURATION + +None needed. + +=head1 AUTHOR + +Olivier Mehani +Based on the debsecan plugin by Nicolas Bouthors. 2016-09-02 + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut # Auto enable if we have deborphan only if [ "$1" = "autoconf" ] ; then - if [ -x /usr/bin/deborphan ]; then + if which deborphan >/dev/null; then echo yes else - echo no + echo "no (deborphan is missing)" fi exit 0 fi # Fail if we don't have deborphan -if [ ! -x /usr/bin/deborphan ]; then +if ! which deborphan >/dev/null; then + echo "deborphan is missing" >&2 exit 1 fi -OUT=`mktemp -t deborphan.XXXXXX` -deborphan --show-section --guess-all | sed 's/\//_/' | sort > ${OUT} +OUT=$(mktemp -t munin-deborphan.XXXXXX) +TMPFILES=$OUT +trap 'rm -f $TMPFILES' EXIT +deborphan --show-section --guess-all | sed 's/\//_/' | sort > "${OUT}" -CATEGORIES="$(sed 's/ .*//' ${OUT} | uniq)" +CATEGORIES="$(awk '{print $1}' "${OUT}" | uniq)" if [ "$1" = "config" ]; then cat < ${TMP} - echo "${cat}.value `cat ${TMP} | wc -l`" - echo "${cat}.extinfo `echo $(cat ${TMP})`" - rm ${TMP} + TMP=$(mktemp -t munin-deborphan.XXXXXX) + TMPFILES="${TMPFILES} $TMP" + sed -n "s/${cat} \+//p" "${OUT}" > "${TMP}" + echo "${cat}.value $(wc -l < "${TMP}")" + # shellcheck disable=SC2005 disable=SC2046 + # echo is not useless here: we want everything on one line + echo "${cat}.extinfo $(echo $(cat "${TMP}"))" done fi - -rm -f ${OUT} From 6d4da015de5d02f3b294321687b72d6bdb918dd6 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sat, 29 Oct 2016 17:43:40 +1100 Subject: [PATCH 156/718] [file_length_] Cleanup and shellcheck Signed-off-by: Olivier Mehani --- plugins/system/file_length_ | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/plugins/system/file_length_ b/plugins/system/file_length_ index a0e94480..f5269584 100755 --- a/plugins/system/file_length_ +++ b/plugins/system/file_length_ @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh : << =cut @@ -18,25 +18,35 @@ Useful for things such as lists (white, black, user, ...). =head1 AUTHOR -Olivier Mehani (based on disk/log_sizes) +Olivier Mehani (based on disk/log_sizes) =head1 LICENSE GPLv2 +=head1 MAGIC MARKERS + + #%# family=manual + =cut -#NAME=`echo $0 | sed 's/.*_//'` -NAME=${0#*_} +# needs shellcheck -x /usr/share/munin/plugins/plugin.sh +# shellcheck source=/usr/share/munin/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +NAME=$(echo "$0" | sed 's/.*_//') TITLE=${title:-File lengths for $NAME} CATEGORY=${category:-system} FILES=${files:-/var/log/messages} -FILES=$(echo $(ls $FILES)) +# we want globbing to happen here +# shellcheck disable=SC2116 disable=SC2086 +FILES=$(echo $FILES) if [ "$1" = "config" ] ; then - if [ ${logarithmic} = 1 ]; then + # shellcheck disable=SC2154 + if [ "${logarithmic}" = "1" ]; then graph_args="-o" else graph_args="-l 0" @@ -50,16 +60,15 @@ graph_vlabel length (lines) EOF for F in $FILES; do - MF=`echo $F | sed 's/[-\/\.]/_/g'` - echo "$MF.label ${F##*/}" + MF=$(clean_fieldname "$F") + BF=$(basename "$F") + echo "$MF.label ${BF}" done else for F in $FILES; do - MF=`echo $F | sed 's/[-\/\.]/_/g'` - echo -n "$MF.value " - cat $F | wc -l - echo -n "$MF.extinfo " - stat --printf="%sB\n" $F + MF=$(echo "$F" | sed 's/[-\/\.]/_/g') + echo "$MF.value $(wc -l < "$F")" + echo "$MF.extinfo $(stat --printf="%sB\n" "$F")" done fi From 17fcfc3736f72ebfaa0b4df12f674c444435490e Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 2 Nov 2016 02:24:55 +0100 Subject: [PATCH 157/718] Rewrited Plugin --- plugins/mail/postfix_stats | 121 +++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 32 deletions(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index 7a2bcd5b..f0690c96 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -3,6 +3,15 @@ # Plugin to show Postfix statistics - needs pflogsumm # # Contributed by David Obando (david@cryptix.de) - 16.04.2007 +# Rewrited by Cristian Deluxe (me@cristiandeluxe.com) - 02.11.2016 +# +# +# Example config for Ubuntu (You need: apt-get install pflogsumm) +# +# [postfix_stats] +# env.logfile /var/log/syslog +# env.logfile2 /var/log/syslog.1 +# env.pflogsumm pflogsumm # # # Magic markers - optional - used by installation scripts and @@ -12,41 +21,89 @@ #%# capabilities=autoconf #set -xv +SYS_LOG=${logfile:-/var/log/syslog} +SYS_LOG2=${logfile2:-/var/log/syslog.0} +PFLOGSUMM=${pflogsumm:-pflogsumm.pl} -case $1 in - config) - cat <<'EOF' -system.type COUNTER -graph_title Postfix statistics -graph_vlabel Postfix statistics -graph_category Mail -graph_total Total -received.label received -delivered.label delivered -forwarded.label forwarded -deferred.label deferred -bounced.label bounced -rejected.label rejected -held.label held -discarded.label discarded -EOF - exit 0;; -esac +# +# Autoconf Section +# +if [ "$1" = "autoconf" ]; then + # Try to find pflogsumm with default name + PFLOG_EXIST=$(command -v pflogsumm.pl) + # Try to find pflogsumm without any extension + if [[ -z "${PFLOG_EXIST}" ]] + then + PFLOG_EXIST=$(command -v pflogsumm) + fi -TMP=`mktemp /tmp/tmp.XXXXXXXX` -pflogsumm.pl --smtpd_stats -d today /var/log/syslog /var/log/syslog.0 | head -n 15 > $TMP + if [[ -z "${PFLOG_EXIST}" ]] + then + echo "no"; + else + echo "yes" + fi + exit 0; +fi -cat < Date: Wed, 2 Nov 2016 04:04:47 +0100 Subject: [PATCH 158/718] Remove whitespaces from result --- plugins/mail/postfix_stats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index f0690c96..a6349347 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -81,7 +81,7 @@ TMP_RAW=$("${PFLOGSUMM}" -d today --detail 0 --zero-fill "${SYS_LOG}" "${SYS_LOG # Return -1 if any error occurs # parseValue() { - TMP_RETURN=$(echo "${TMP_RAW}" | grep -Ei '^[[:space:]]+[[:digit:]]+[[:space:]]+'"${1}"'.*$' | grep -oEi '[[:digit:]]+[[:space:]]+') + TMP_RETURN=$(echo "${TMP_RAW}" | grep -Ei '^[[:space:]]+[[:digit:]]+[[:space:]]+'"${1}"'.*$' | grep -oEi '[[:digit:]]+[[:space:]]+' | sed 's: ::g') if [[ -z "${TMP_RETURN}" ]] then echo -1 From 918602cd00a1faf9e622f2a6f8a65c5596aeb03c Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 2 Nov 2016 04:53:27 +0100 Subject: [PATCH 159/718] Use simple quotes and better graph config --- plugins/mail/postfix_stats | 82 +++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index a6349347..d7f5d049 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -28,7 +28,7 @@ PFLOGSUMM=${pflogsumm:-pflogsumm.pl} # # Autoconf Section # -if [ "$1" = "autoconf" ]; then +if [ "$1" = 'autoconf' ]; then # Try to find pflogsumm with default name PFLOG_EXIST=$(command -v pflogsumm.pl) @@ -40,9 +40,9 @@ if [ "$1" = "autoconf" ]; then if [[ -z "${PFLOG_EXIST}" ]] then - echo "no"; + echo 'no'; else - echo "yes" + echo 'yes' fi exit 0; fi @@ -50,20 +50,36 @@ fi # # Config Section # -if [ "$1" = "config" ]; then - echo "system.type COUNTER" - echo "graph_title Postfix statistics" - echo "graph_vlabel Postfix statistics" - echo "graph_category Mail" - echo "graph_total Total" - echo "received.label received" - echo "delivered.label delivered" - echo "forwarded.label forwarded" - echo "deferred.label deferred" - echo "bounced.label bounced" - echo "rejected.label rejected" - echo "held.label held" - echo "discarded.label discarded" +if [ "$1" = 'config' ]; then + echo 'graph_title Postfix statistics' + echo 'graph_vlabel Postfix statistics' + echo 'graph_category mail' + echo 'graph_scale no' + echo 'graph_total Total' + echo 'received.label received' + echo 'received.type DERIVE' + echo 'received.min 0' + echo 'delivered.label delivered' + echo 'delivered.type DERIVE' + echo 'delivered.min 0' + echo 'forwarded.label forwarded' + echo 'forwarded.type DERIVE' + echo 'forwarded.min 0' + echo 'deferred.label deferred' + echo 'deferred.type DERIVE' + echo 'deferred.min 0' + echo 'bounced.label bounced' + echo 'bounced.type DERIVE' + echo 'bounced.min 0' + echo 'rejected.label rejected' + echo 'rejected.type DERIVE' + echo 'rejected.min 0' + echo 'held.label held' + echo 'held.type DERIVE' + echo 'held.min 0' + echo 'discarded.label discarded' + echo 'discarded.type DERIVE' + echo 'discarded.min 0' exit 0; fi @@ -91,19 +107,19 @@ parseValue() { } # Print results -printf "received.value " -parseValue "received" -printf "delivered.value " -parseValue "delivered" -printf "forwarded.value " -parseValue "forwarded" -printf "deferred.value " -parseValue "deferred" -printf "bounced.value " -parseValue "bounced" -printf "rejected.value " -parseValue "rejected" -printf "held.value " -parseValue "held" -printf "discarded.value " -parseValue "discarded" +printf 'received.value ' +parseValue 'received' +printf 'delivered.value ' +parseValue 'delivered' +printf 'forwarded.value ' +parseValue 'forwarded' +printf 'deferred.value ' +parseValue 'deferred' +printf 'bounced.value ' +parseValue 'bounced' +printf 'rejected.value ' +parseValue 'rejected' +printf 'held.value ' +parseValue 'held' +printf 'discarded.value ' +parseValue 'discarded' From e75132a7693f4166d64913e2fdaf0a66b07af18b Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 2 Nov 2016 05:03:01 +0100 Subject: [PATCH 160/718] change spaces for tabs (code indent) --- plugins/mail/postfix_stats | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index d7f5d049..76ea7cd7 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -57,29 +57,29 @@ if [ "$1" = 'config' ]; then echo 'graph_scale no' echo 'graph_total Total' echo 'received.label received' - echo 'received.type DERIVE' - echo 'received.min 0' + echo 'received.type DERIVE' + echo 'received.min 0' echo 'delivered.label delivered' - echo 'delivered.type DERIVE' - echo 'delivered.min 0' + echo 'delivered.type DERIVE' + echo 'delivered.min 0' echo 'forwarded.label forwarded' - echo 'forwarded.type DERIVE' - echo 'forwarded.min 0' + echo 'forwarded.type DERIVE' + echo 'forwarded.min 0' echo 'deferred.label deferred' - echo 'deferred.type DERIVE' - echo 'deferred.min 0' + echo 'deferred.type DERIVE' + echo 'deferred.min 0' echo 'bounced.label bounced' - echo 'bounced.type DERIVE' - echo 'bounced.min 0' + echo 'bounced.type DERIVE' + echo 'bounced.min 0' echo 'rejected.label rejected' - echo 'rejected.type DERIVE' - echo 'rejected.min 0' + echo 'rejected.type DERIVE' + echo 'rejected.min 0' echo 'held.label held' - echo 'held.type DERIVE' - echo 'held.min 0' + echo 'held.type DERIVE' + echo 'held.min 0' echo 'discarded.label discarded' - echo 'discarded.type DERIVE' - echo 'discarded.min 0' + echo 'discarded.type DERIVE' + echo 'discarded.min 0' exit 0; fi From 9eb4cc1f01f953b89934caf4e80a441f2a707fa2 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 2 Nov 2016 05:07:45 +0100 Subject: [PATCH 161/718] =?UTF-8?q?Chenged=20family=20to=20=E2=80=9Ccontri?= =?UTF-8?q?b=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/mail/postfix_stats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index 76ea7cd7..d495d002 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -17,7 +17,7 @@ # Magic markers - optional - used by installation scripts and # munin-config: # -#%# family=manual +#%# family=contrib #%# capabilities=autoconf #set -xv From 8e2025a9b932b540260710d5c637bccd47e2eb56 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 2 Nov 2016 06:34:57 +0100 Subject: [PATCH 162/718] changed graph_period to minute (more appropiated) --- plugins/mail/postfix_stats | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index d495d002..9f56db34 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -55,6 +55,7 @@ if [ "$1" = 'config' ]; then echo 'graph_vlabel Postfix statistics' echo 'graph_category mail' echo 'graph_scale no' + echo 'graph_period minute' echo 'graph_total Total' echo 'received.label received' echo 'received.type DERIVE' From 48ab33220e594d41e7d13a7b75f7b92f7612fd72 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 2 Nov 2016 06:59:07 +0100 Subject: [PATCH 163/718] no duplicated code, area graph, only first result --- plugins/mail/postfix_stats | 62 ++++++++++++-------------------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index 9f56db34..97473a74 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -25,6 +25,9 @@ SYS_LOG=${logfile:-/var/log/syslog} SYS_LOG2=${logfile2:-/var/log/syslog.0} PFLOGSUMM=${pflogsumm:-pflogsumm.pl} +# Fields (Array to avoid code duplication) +declare -a FIELDS_ARR=("received" "delivered" "forwarded" "deferred" "bounced" "rejected" "held" "discarded") + # # Autoconf Section # @@ -57,31 +60,17 @@ if [ "$1" = 'config' ]; then echo 'graph_scale no' echo 'graph_period minute' echo 'graph_total Total' - echo 'received.label received' - echo 'received.type DERIVE' - echo 'received.min 0' - echo 'delivered.label delivered' - echo 'delivered.type DERIVE' - echo 'delivered.min 0' - echo 'forwarded.label forwarded' - echo 'forwarded.type DERIVE' - echo 'forwarded.min 0' - echo 'deferred.label deferred' - echo 'deferred.type DERIVE' - echo 'deferred.min 0' - echo 'bounced.label bounced' - echo 'bounced.type DERIVE' - echo 'bounced.min 0' - echo 'rejected.label rejected' - echo 'rejected.type DERIVE' - echo 'rejected.min 0' - echo 'held.label held' - echo 'held.type DERIVE' - echo 'held.min 0' - echo 'discarded.label discarded' - echo 'discarded.type DERIVE' - echo 'discarded.min 0' - exit 0; + + # Generate config for each field + for i in "${FIELDS_ARR[@]}" + do + echo "${i}.label ${i}" + echo "${i}.type DERIVE" + echo "${i}.min 0" + echo "${i}.draw AREA" + done + + exit 0 fi # @@ -98,7 +87,7 @@ TMP_RAW=$("${PFLOGSUMM}" -d today --detail 0 --zero-fill "${SYS_LOG}" "${SYS_LOG # Return -1 if any error occurs # parseValue() { - TMP_RETURN=$(echo "${TMP_RAW}" | grep -Ei '^[[:space:]]+[[:digit:]]+[[:space:]]+'"${1}"'.*$' | grep -oEi '[[:digit:]]+[[:space:]]+' | sed 's: ::g') + TMP_RETURN=$(echo "${TMP_RAW}" | grep -Ei '^[[:space:]]+[[:digit:]]+[[:space:]]+'"${1}"'.*$' | grep -oEi '[[:digit:]]+[[:space:]]+' | head -n 1 | sed 's: ::g') if [[ -z "${TMP_RETURN}" ]] then echo -1 @@ -108,19 +97,8 @@ parseValue() { } # Print results -printf 'received.value ' -parseValue 'received' -printf 'delivered.value ' -parseValue 'delivered' -printf 'forwarded.value ' -parseValue 'forwarded' -printf 'deferred.value ' -parseValue 'deferred' -printf 'bounced.value ' -parseValue 'bounced' -printf 'rejected.value ' -parseValue 'rejected' -printf 'held.value ' -parseValue 'held' -printf 'discarded.value ' -parseValue 'discarded' +for i in "${FIELDS_ARR[@]}" +do + printf "${i}.value " + parseValue "${i}" +done From ce23bdde592d3c43b8ff8b45e592cc5fc938c8f2 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 2 Nov 2016 07:00:45 +0100 Subject: [PATCH 164/718] Changed draw mode to STACK --- plugins/mail/postfix_stats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index 97473a74..afb508fe 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -67,7 +67,7 @@ if [ "$1" = 'config' ]; then echo "${i}.label ${i}" echo "${i}.type DERIVE" echo "${i}.min 0" - echo "${i}.draw AREA" + echo "${i}.draw STACK" done exit 0 From 87a6a775b35fe31a4887497059c908e4efe8b8f6 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 2 Nov 2016 07:06:36 +0100 Subject: [PATCH 165/718] draw mode changed to AREASTACK --- plugins/mail/postfix_stats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index afb508fe..d21fd78e 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -67,7 +67,7 @@ if [ "$1" = 'config' ]; then echo "${i}.label ${i}" echo "${i}.type DERIVE" echo "${i}.min 0" - echo "${i}.draw STACK" + echo "${i}.draw AREASTACK" done exit 0 From d1d668f6bc3b58172951f44bd8dbd4317a30194f Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Fri, 4 Nov 2016 05:11:19 +0100 Subject: [PATCH 166/718] Strict SH script, thanks to sumpfralle --- plugins/mail/postfix_stats | 121 ++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 48 deletions(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index d21fd78e..6cfd1bef 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -1,53 +1,81 @@ #!/bin/sh -# -# Plugin to show Postfix statistics - needs pflogsumm -# -# Contributed by David Obando (david@cryptix.de) - 16.04.2007 -# Rewrited by Cristian Deluxe (me@cristiandeluxe.com) - 02.11.2016 -# -# -# Example config for Ubuntu (You need: apt-get install pflogsumm) -# -# [postfix_stats] -# env.logfile /var/log/syslog -# env.logfile2 /var/log/syslog.1 -# env.pflogsumm pflogsumm -# -# -# Magic markers - optional - used by installation scripts and -# munin-config: -# -#%# family=contrib -#%# capabilities=autoconf +# -*- sh -*- + +: <<=cut + +=head1 NAME + +postfix_stats - Munin plugin to monitor postfix statistics. + +=head1 APPLICABLE SYSTEMS + +Any system where pflogsumm script can be executed. + +=head1 CONFIGURATION + +There is no default configuration. This is an example config for Ubuntu: + + [postfix_stats] + env.logfiles /var/log/syslog /var/log/syslog.1 + env.pflogsumm pflogsumm + +env.logfiles contains space separated syslog logfiles, usually current log +and previous log. You can add more log files if you want, but this can +increase the time required to parse it. + +env.pflogsumm The "pflogsumm" script, can be pflogsumm.pl if are manually +downloaded and installed or pflogsumm if are installed by apt-get. + +=head1 INTERPRETATION + +This plugin adds up the RSS of all processes matching the +regex given as the process name, as reported by ps. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + 0.2 plugin completely rewritten + 0.1 first release. + +=head1 BUGS + +None known + +=head1 AUTHOR + +Originally: David Obando (david@cryptix.de) +Modified by: github.com/cristiandeluxe +Thanks to: sumpfralle + +=head1 LICENSE + +GPLv2 + +=cut #set -xv -SYS_LOG=${logfile:-/var/log/syslog} -SYS_LOG2=${logfile2:-/var/log/syslog.0} -PFLOGSUMM=${pflogsumm:-pflogsumm.pl} +SYS_LOG="${logfiles:-/var/log/syslog /var/log/syslog.0}" +PFLOGSUMM="${pflogsum}" +[ -z "$PFLOGSUMM" ] && PFLOGSUMM="$(which pflogsumm pflogsumm.pl | head -1)" -# Fields (Array to avoid code duplication) -declare -a FIELDS_ARR=("received" "delivered" "forwarded" "deferred" "bounced" "rejected" "held" "discarded") +# Fields (Array to avoid code duplication) must be space separated +FIELDS_ARR="received delivered forwarded deferred bounced rejected held discarded" # # Autoconf Section # if [ "$1" = 'autoconf' ]; then - # Try to find pflogsumm with default name - PFLOG_EXIST=$(command -v pflogsumm.pl) - - # Try to find pflogsumm without any extension - if [[ -z "${PFLOG_EXIST}" ]] + # Check if pflogsumm exist + if [ -z "${PFLOGSUMM}" ] then - PFLOG_EXIST=$(command -v pflogsumm) - fi - - if [[ -z "${PFLOG_EXIST}" ]] - then - echo 'no'; + echo 'no (pflogsum not found in your system)' else echo 'yes' fi - exit 0; + exit 0 fi # @@ -62,8 +90,7 @@ if [ "$1" = 'config' ]; then echo 'graph_total Total' # Generate config for each field - for i in "${FIELDS_ARR[@]}" - do + for i in $FIELDS_ARR; do echo "${i}.label ${i}" echo "${i}.type DERIVE" echo "${i}.min 0" @@ -78,27 +105,25 @@ fi # # Variable to store the pflogsumm result. -TMP_RAW=$("${PFLOGSUMM}" -d today --detail 0 --zero-fill "${SYS_LOG}" "${SYS_LOG2}") +TMP_RAW=$(eval "${PFLOGSUMM} -d today --detail 0 --zero-fill ${SYS_LOG}") # Parse value from Raw result # # Return digit if regex are parsed correctly # -# Return -1 if any error occurs +# Return U (undefined) if any error occurs # parseValue() { - TMP_RETURN=$(echo "${TMP_RAW}" | grep -Ei '^[[:space:]]+[[:digit:]]+[[:space:]]+'"${1}"'.*$' | grep -oEi '[[:digit:]]+[[:space:]]+' | head -n 1 | sed 's: ::g') - if [[ -z "${TMP_RETURN}" ]] - then - echo -1 + local TMP_RETURN=$(echo "${TMP_RAW}" | grep -Ei "^[[:space:]]+[[:digit:]]+[[:space:]]+${1}.*$" | grep -oEi '[[:digit:]]+[[:space:]]+' | head -n 1 | sed 's: ::g') + if [ -z "${TMP_RETURN}" ]; then + echo 'U' else echo "${TMP_RETURN}" fi } # Print results -for i in "${FIELDS_ARR[@]}" -do - printf "${i}.value " +for i in $FIELDS_ARR; do + printf "%s.value " "${i}" parseValue "${i}" done From daf5b94a07cf6043417e00f04d3ff3715a8e9cdb Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Fri, 4 Nov 2016 10:33:14 +0100 Subject: [PATCH 167/718] =?UTF-8?q?Sellcheck=20disables=20and=20=E2=80=9Ce?= =?UTF-8?q?val=E2=80=9D=20removed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/mail/postfix_stats | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index 6cfd1bef..6e3a3a3e 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -58,6 +58,8 @@ GPLv2 #set -xv SYS_LOG="${logfiles:-/var/log/syslog /var/log/syslog.0}" + +# shellcheck disable=SC2154 PFLOGSUMM="${pflogsum}" [ -z "$PFLOGSUMM" ] && PFLOGSUMM="$(which pflogsumm pflogsumm.pl | head -1)" @@ -105,7 +107,8 @@ fi # # Variable to store the pflogsumm result. -TMP_RAW=$(eval "${PFLOGSUMM} -d today --detail 0 --zero-fill ${SYS_LOG}") +# shellcheck disable=SC2086 +TMP_RAW=$("${PFLOGSUMM}" -d today --detail 0 --zero-fill ${SYS_LOG}) # Parse value from Raw result # @@ -114,6 +117,7 @@ TMP_RAW=$(eval "${PFLOGSUMM} -d today --detail 0 --zero-fill ${SYS_LOG}") # Return U (undefined) if any error occurs # parseValue() { + # shellcheck disable=SC2155 local TMP_RETURN=$(echo "${TMP_RAW}" | grep -Ei "^[[:space:]]+[[:digit:]]+[[:space:]]+${1}.*$" | grep -oEi '[[:digit:]]+[[:space:]]+' | head -n 1 | sed 's: ::g') if [ -z "${TMP_RETURN}" ]; then echo 'U' From 156f0d840e3f7f256bb3a86a6b5ffec64c34e8a6 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Fri, 4 Nov 2016 10:48:06 +0100 Subject: [PATCH 168/718] Small language fixes --- plugins/mail/postfix_stats | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats index 6e3a3a3e..d3e5ae91 100755 --- a/plugins/mail/postfix_stats +++ b/plugins/mail/postfix_stats @@ -13,23 +13,25 @@ Any system where pflogsumm script can be executed. =head1 CONFIGURATION -There is no default configuration. This is an example config for Ubuntu: +There is no default configuration. This is an example config for Ubuntu: [postfix_stats] env.logfiles /var/log/syslog /var/log/syslog.1 env.pflogsumm pflogsumm env.logfiles contains space separated syslog logfiles, usually current log -and previous log. You can add more log files if you want, but this can -increase the time required to parse it. +and previous log. You can add more log files if you want, but this may +increase the time required for analysis. -env.pflogsumm The "pflogsumm" script, can be pflogsumm.pl if are manually -downloaded and installed or pflogsumm if are installed by apt-get. +env.pflogsumm The "pflogsumm" script, can be pflogsumm.pl if it was manually +downloaded and installed, or "pflogsumm" if it was installed by a package +manager (like apt-get). =head1 INTERPRETATION -This plugin adds up the RSS of all processes matching the -regex given as the process name, as reported by ps. +This plugin displays the messages: received, delivered, rejected... +Average per minute is made and it shows the total message count. +It is useful to know the load and messages being processed. =head1 MAGIC MARKERS From ddbb4782ecb7d82e13e0fecd389f6dabde3acb9d Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sat, 5 Nov 2016 21:48:55 +1100 Subject: [PATCH 169/718] [deborphan] Don't use tempfiles, and other fixes Signed-off-by: Olivier Mehani --- plugins/system/deborphan | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/plugins/system/deborphan b/plugins/system/deborphan index e063de53..0fe38591 100755 --- a/plugins/system/deborphan +++ b/plugins/system/deborphan @@ -30,6 +30,9 @@ GPLv2 =cut +# shellcheck disable=SC1090 +. "$MUNIN_LIBDIR/plugins/plugin.sh" + # Auto enable if we have deborphan only if [ "$1" = "autoconf" ] ; then if which deborphan >/dev/null; then @@ -46,12 +49,9 @@ if ! which deborphan >/dev/null; then exit 1 fi -OUT=$(mktemp -t munin-deborphan.XXXXXX) -TMPFILES=$OUT -trap 'rm -f $TMPFILES' EXIT -deborphan --show-section --guess-all | sed 's/\//_/' | sort > "${OUT}" +OUT=$(deborphan --show-section --guess-all | sort) -CATEGORIES="$(awk '{print $1}' "${OUT}" | uniq)" +CATEGORIES="$(echo "${OUT}" | awk '{print $1}' | uniq)" if [ "$1" = "config" ]; then cat < "${TMP}" - echo "${cat}.value $(wc -l < "${TMP}")" - # shellcheck disable=SC2005 disable=SC2046 - # echo is not useless here: we want everything on one line - echo "${cat}.extinfo $(echo $(cat "${TMP}"))" + for CAT in ${CATEGORIES}; do + CATFIELD=$(clean_fieldname "${CAT}") + CATDATA=$(echo "${OUT}" | sed -n "s#${CAT} \+##p") + echo "${CATFIELD}.value $(echo "${CATDATA}" | wc -l)" + echo "${CATFIELD}.extinfo $(echo "${CATDATA}" | tr '\n' ' ')" done fi From 000c97ccece6cd644f642085cbda1dc40ae35742 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sat, 5 Nov 2016 21:51:10 +1100 Subject: [PATCH 170/718] [deborphan] No longer dependent on bashisms Signed-off-by: Olivier Mehani --- plugins/system/deborphan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/system/deborphan b/plugins/system/deborphan index 0fe38591..10d5ac44 100755 --- a/plugins/system/deborphan +++ b/plugins/system/deborphan @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh : << =cut From 2d13d2a50d7c4367186996e5dce4bd600b8e59d6 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Sat, 5 Nov 2016 14:51:14 -0700 Subject: [PATCH 171/718] Add documentation and example graphs for the tc_ plugin --- plugins/network/example-graphs/tc_-day.png | Bin 0 -> 27195 bytes plugins/network/example-graphs/tc_-week.png | Bin 0 -> 41662 bytes plugins/network/tc_.pod | 44 ++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 plugins/network/example-graphs/tc_-day.png create mode 100644 plugins/network/example-graphs/tc_-week.png create mode 100644 plugins/network/tc_.pod diff --git a/plugins/network/example-graphs/tc_-day.png b/plugins/network/example-graphs/tc_-day.png new file mode 100644 index 0000000000000000000000000000000000000000..6607b71db60be7f119a17cd693d47a0efe57dd2c GIT binary patch literal 27195 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 literal 0 HcmV?d00001 diff --git a/plugins/network/example-graphs/tc_-week.png b/plugins/network/example-graphs/tc_-week.png new file mode 100644 index 0000000000000000000000000000000000000000..50557672ea50a73ad21e964e50d486cf5d77c6b4 GIT binary patch literal 41662 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 literal 0 HcmV?d00001 diff --git a/plugins/network/tc_.pod b/plugins/network/tc_.pod new file mode 100644 index 00000000..be5fc5a9 --- /dev/null +++ b/plugins/network/tc_.pod @@ -0,0 +1,44 @@ +#!/usr/bin/perl -w + +=pod + +=encoding UTF-8 + +=head1 NAME + +tc_ - Plugin to monitor traffic control queue class bandwidth usage + +=head1 APPLICABLE SYSTEMS + +Linux 2.5.40+, 2.6.x, 3.x, 4.x + +=head1 CONFIGURATION + +None needed. + +=head1 INTERPRETATION + +Traffic control is the name given to the sets of queuing systems and mechanisms by which packets are received and transmitted on a router. This includes deciding which (and whether) packets to accept at what rate on the input of an interface and determining which packets to transmit in what order at what rate on the output of an interface. + +This plugin monitors the bandwidth used by each queue class. The root class will draw as a single line, whereas children will be drawn in a stacked graph. Complex hierarchies which have more than the root and its direct children are not fully supported and may not render correctly. + +=head1 SEE ALSO + +"man tc" and "tc -s class show dev " to get more information about tc and to see the format of the statistics being parsed. + +=head1 MAGIC MARKERS + + #%# family=manual + #%# capabilities=autoconf + +=head1 AUTHORS + +Steve Schnepp , +Samuel Smith , +Nye Liu + +=head1 LICENSE + +GPLv2 or later + +=cut From 6cb9ba170fc66979ffd46990e19d645daaefd8a1 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Sat, 5 Nov 2016 14:52:03 -0700 Subject: [PATCH 172/718] Cleanup suggested by maintainer * family should be auto, not manual * Combine multiple sed scripts into a single script * Double quote sh variable dereferences * Don't use deprecated backquotes --- plugins/network/tc_ | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index a870bf3c..3b373434 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -9,17 +9,17 @@ # Magic markers (optional - used by munin-config and some installation # scripts): # -#%# family=manual +#%# family=auto #%# capabilities=autoconf suggest DEVICE=${0##*/tc_} mytc() { - /sbin/tc -s class show dev $1 | tr "\n" "|" | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n + /sbin/tc -s class show dev "$1" | tr "\n" "|" | sed -e "s/ \+/ /g; s/ |/|/g; s/| /|/g; s/||/\n/g; s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n } -case $1 in +case "$1" in autoconf) if [ -r /proc/net/dev ]; then echo yes @@ -36,13 +36,11 @@ case $1 in split($0, a, /: */); gsub(/^ +/,"",a[1]); if (($2 > 0) || ($10 > 0)) print a[1]; }' /proc/net/dev - -# egrep '^ *(eth|tap|bond|wlan|ath|ra|sw)[0-9]+:' /proc/net/dev | cut -f1 -d: | sed 's/ //g' fi exit 0 ;; config) - echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 }' | tr "\n" " "`" + echo "graph_order $(mytc "$DEVICE" | awk '{ print $2 "_" $3 }' | tr "\n" " ")" echo "graph_title $DEVICE TC traffic" echo 'graph_args --base 1000' echo 'graph_vlabel bits per ${graph_period}' @@ -50,7 +48,7 @@ case $1 in echo "graph_info This graph shows the TC classes traffic of the $DEVICE network interface. Please note that the traffic is shown in bits per second, not bytes." # the root(s) - mytc $DEVICE | grep -v " parent " | tr "_" " " | awk '{ + mytc "$DEVICE" | grep -v " parent " | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4; print $2 "_" $3 "_" $4 ".type DERIVE"; print $2 "_" $3 "_" $4 ".min 0"; @@ -58,7 +56,7 @@ case $1 in }' # TODO: only AREASTACK things with no children # the child(s) - mytc $DEVICE | grep " parent " | tr "_" " " | awk '{ + mytc "$DEVICE" | grep " parent " | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4; print $2 "_" $3 "_" $4 ".type DERIVE"; print $2 "_" $3 "_" $4 ".min 0"; @@ -70,13 +68,13 @@ case $1 in esac # the root(s) -mytc $DEVICE | grep -v " parent " | awk '{ +mytc "$DEVICE" | grep -v " parent " | awk '{ split(substr($0, match($0, /[0-9]+ [Bb]ytes/)), a, " "); print $2 "_" $3 ".value " a[1]; }' # the child(s) -mytc $DEVICE | grep " parent " | awk '{ +mytc "$DEVICE" | grep " parent " | awk '{ split(substr($0, match($0, /[0-9]+ [Bb]ytes/)), a, " "); print $2 "_" $3 ".value " a[1]; }' From fd25056ffd6aa3eb23a9a30315b5a311b4e45038 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Sat, 5 Nov 2016 14:57:45 -0700 Subject: [PATCH 173/718] Set family to auto in tc_.pod as well --- plugins/network/tc_.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/network/tc_.pod b/plugins/network/tc_.pod index be5fc5a9..74821bd9 100644 --- a/plugins/network/tc_.pod +++ b/plugins/network/tc_.pod @@ -28,7 +28,7 @@ This plugin monitors the bandwidth used by each queue class. The root class will =head1 MAGIC MARKERS - #%# family=manual + #%# family=auto #%# capabilities=autoconf =head1 AUTHORS From c4e03516ce5ae3bb5eb59bdd3f86197f62c7eafe Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Sat, 5 Nov 2016 14:58:49 -0700 Subject: [PATCH 174/718] Forgot the suggest tag --- plugins/network/tc_.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/network/tc_.pod b/plugins/network/tc_.pod index 74821bd9..70058d8c 100644 --- a/plugins/network/tc_.pod +++ b/plugins/network/tc_.pod @@ -29,7 +29,7 @@ This plugin monitors the bandwidth used by each queue class. The root class will =head1 MAGIC MARKERS #%# family=auto - #%# capabilities=autoconf + #%# capabilities=autoconf suggest =head1 AUTHORS From 07fb93c2a4251e7916160a835fd6c6ffd95a2479 Mon Sep 17 00:00:00 2001 From: Olivier Mehani Date: Sun, 6 Nov 2016 13:18:56 +1100 Subject: [PATCH 175/718] [fresh_backup] Add missing reference to backup-manager Signed-off-by: Olivier Mehani --- plugins/backup/fresh-backups | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/backup/fresh-backups b/plugins/backup/fresh-backups index ce46ae7c..5e54cabf 100755 --- a/plugins/backup/fresh-backups +++ b/plugins/backup/fresh-backups @@ -29,10 +29,14 @@ This will also set the warning and critical values for this plugin to 2*4 and 4, respectively, meaning that if the number of fresh files goes below those limits, the relevant notifications will be triggerred. +An example configuration snippet for backup-manager [0] follows. + export BM_REPOSITORY_ROOT="/path/to/your/backups" export BM_TARBALL_FILETYPE="tar.bz2" export BM_TARBALL_DIRECTORIES="/etc /home /srv /data" +[0] https://github.com/sukria/Backup-Manager + =head1 AUTHOR Olivier Mehani From 49b79ca228f3a0d71ed3603162c5b23b8679ef80 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Sat, 5 Nov 2016 21:08:01 -0700 Subject: [PATCH 176/718] Embed tc_ pod documentation into the plugin itself --- plugins/network/tc_.pod | 44 ----------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 plugins/network/tc_.pod diff --git a/plugins/network/tc_.pod b/plugins/network/tc_.pod deleted file mode 100644 index 70058d8c..00000000 --- a/plugins/network/tc_.pod +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/perl -w - -=pod - -=encoding UTF-8 - -=head1 NAME - -tc_ - Plugin to monitor traffic control queue class bandwidth usage - -=head1 APPLICABLE SYSTEMS - -Linux 2.5.40+, 2.6.x, 3.x, 4.x - -=head1 CONFIGURATION - -None needed. - -=head1 INTERPRETATION - -Traffic control is the name given to the sets of queuing systems and mechanisms by which packets are received and transmitted on a router. This includes deciding which (and whether) packets to accept at what rate on the input of an interface and determining which packets to transmit in what order at what rate on the output of an interface. - -This plugin monitors the bandwidth used by each queue class. The root class will draw as a single line, whereas children will be drawn in a stacked graph. Complex hierarchies which have more than the root and its direct children are not fully supported and may not render correctly. - -=head1 SEE ALSO - -"man tc" and "tc -s class show dev " to get more information about tc and to see the format of the statistics being parsed. - -=head1 MAGIC MARKERS - - #%# family=auto - #%# capabilities=autoconf suggest - -=head1 AUTHORS - -Steve Schnepp , -Samuel Smith , -Nye Liu - -=head1 LICENSE - -GPLv2 or later - -=cut From 66d48afbbb2f99ed10a0cb548fc681cbc3f5e482 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 6 Nov 2016 12:37:28 +0100 Subject: [PATCH 177/718] [asterisk] fix codecs accounting (Closes: #699) The following issues are fixed by lelutin: * line ending matching does not match for lines that only use \n as line ending * iteration is broken because the index for setting values in the codecs accounting array is never reset to 0 before new loops. * iax channels don't use a codec hex value, but rather their names (sip still uses the hex values as far as I can see) see https://github.com/munin-monitoring/contrib/issues/699 --- plugins/asterisk/asterisk | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 7a3c13ce..e6581e11 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -320,11 +320,12 @@ END } # split the channels' listing and drop header and footnotes - my @sipchannels = $sipchannels_response ? split(/\r\n/, $sipchannels_response) : (); + my @sipchannels = $sipchannels_response ? split(/\r\n|\n/, $sipchannels_response) : (); pop(@sipchannels); shift(@sipchannels); - my @iaxchannels = $iaxchannels_response ? split(/\r\n/, $iaxchannels_response) : (); + my @iaxchannels = $iaxchannels_response ? split(/\r\n|\n/, $iaxchannels_response) : (); pop(@iaxchannels); shift(@iaxchannels); + $i = 0; foreach my $sipchan (@sipchannels) { my $found = 0; my @fields = split ' ', $sipchan; @@ -346,6 +347,7 @@ END } } + $i = 0; foreach my $iaxchan (@iaxchannels) { my $found = 0; my @fields = split ' ', $iaxchan; @@ -354,7 +356,7 @@ END $unknown += 1; next; } - foreach my $codec (@CODECSX) { + foreach my $codec (@CODECS) { if ($fields[8] eq "$codec") { $results[$i] = $results[$i] + 1; $found = 1; From 04def756bc906ebb22cb55f64fbf3ea31f943372 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 6 Nov 2016 13:46:07 +0100 Subject: [PATCH 178/718] [asterisk] cleanup linebreak handling and single/plurals (Closes: #698) lelutin proposed some changes: * the total is 0 if there is only one channel active (due to "channel" vs. "channels") * linebreaks "\r\n" are expected See https://github.com/munin-monitoring/contrib/issues/698 --- plugins/asterisk/asterisk | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index e6581e11..75a203ed 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -85,8 +85,8 @@ sub asterisk_command { # Response: (Error|Follows|???) $line = $socket->getline; - if ($line ne "Response: Follows\r\n") { - while ( $line = $socket->getline and $line ne "\r\n" ) {} + if ($line !~ /^Response: Follows\r?\n$/) { + while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} return undef; } @@ -94,12 +94,12 @@ sub asterisk_command { $line = $socket->getline; # Until we get the --END COMMAND-- marker, it's the command's output. - while ( $line = $socket->getline and $line ne "--END COMMAND--\r\n" ) { + while ( $line = $socket->getline and $line !~ /^--END COMMAND--\r?\n$/ ) { $reply .= $line; } # And then wait for the empty line that says we're done - while ( $line = $socket->getline and $line ne "\r\n" ) {} + while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} return $reply; } @@ -130,13 +130,13 @@ if ( $socket ) { $socket->print("Action: login\nUsername: $username\nSecret: $secret\nEvents: off\n\n"); my $response_status = $socket->getline; - if ( $response_status ne "Response: Success\r\n" ) { + if ( $response_status !~ /^Response: Success\r?\n$/ ) { my $response_message = $socket->getline; - $response_message =~ s/Message: (.*)\r\n/$1/; + $response_message =~ s/Message: (.*)\r?\n/$1/; $error = "Asterisk authentication error: " . $response_message; } - while ( $line = $socket->getline and $line ne "\r\n" ) {} + while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} } if ( $ARGV[0] and $ARGV[0] eq 'autoconf' ) { @@ -251,7 +251,7 @@ my $iaxchannels_response = asterisk_command($socket, "iax2 show channels"); $socket->close(); my $active_channels = 'U'; -$active_channels = $1 if $channels_response =~ /\n([0-9]+) active channels/; +$active_channels = $1 if $channels_response =~ /\n([0-9]+) active channels?/; print < Date: Sun, 6 Nov 2016 13:56:45 +0100 Subject: [PATCH 179/718] [asterisk] fix inconsistent fieldname for voicemail messages (Closes: #700) lelutin proposed the change of the fieldname "messages" in the config section to "total" (as used in the "fetch" output) see https://github.com/munin-monitoring/contrib/issues/700 --- plugins/asterisk/asterisk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 75a203ed..19443cec 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -174,7 +174,7 @@ graph_title Asterisk voicemail messages graph_args --base 1000 -l 0 graph_vlabel messages graph_category asterisk -messages.label Total messages +total.label Total messages END print < Date: Mon, 9 Nov 2015 21:32:46 -0300 Subject: [PATCH 180/718] proc_mem_by_user: - use of bash - don't corrupt rrd files when "root" (or any unclean value) is requested --- plugins/processes/proc_mem_by_user | 64 ++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 20 deletions(-) mode change 100644 => 100755 plugins/processes/proc_mem_by_user diff --git a/plugins/processes/proc_mem_by_user b/plugins/processes/proc_mem_by_user old mode 100644 new mode 100755 index 5e763288..567e386f --- a/plugins/processes/proc_mem_by_user +++ b/plugins/processes/proc_mem_by_user @@ -1,5 +1,6 @@ #!/bin/sh # +# (c) 2015, Raphaël Droz # (c) 2014, Gilles Fauvie # Based on the 'du_multidirs' plugin, written by Christian Kujau # @@ -10,33 +11,56 @@ # [proc_mem_by_user] # env.users munin-node jprod # +# see bug: +# http://munin-monitoring.org/ticket/921 +# /usr/share/munin/plugins/plugin.sh (clean_fieldname()) -. $MUNIN_LIBDIR/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" +users=${users:-munin-node} -if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + +if [ "$1" = autoconf ]; then + ok=1 + [ -z "$users" ] && ok=0 + + for user in $users; do + ps u -U "$user" 1> /dev/null 2>&1 || ok=0 + done + + if [ $ok = 1 ]; then echo yes + else echo no; fi + exit 0 fi -users=${users:="munin-node"} +if [ "$1" = config ]; then + cat < 1 { sum += $6 }; END { print sum * 1024 }'` -done \ No newline at end of file + munin_safe_name=$(clean_fieldname "$user") + sum=$(ps u -U "$user" | awk 'BEGIN { sum = 0 } NR > 1 { sum += $6 }; END { print sum * 1024 }') + echo "$munin_safe_name.value ${sum:-U}" +done From 22d907b92ee48e6c72a049006388891a43e7c65c Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Mon, 7 Nov 2016 11:19:48 +0100 Subject: [PATCH 181/718] added repsonsive template, which shows month and year graphs on wide screens --- templates/responsive/munin-categoryview.tmpl | 55 +++++++ .../responsive/munin-comparison-day.tmpl | 44 ++++++ .../responsive/munin-comparison-month.tmpl | 42 ++++++ .../responsive/munin-comparison-week.tmpl | 44 ++++++ .../responsive/munin-comparison-year.tmpl | 42 ++++++ templates/responsive/munin-domainview.tmpl | 72 +++++++++ templates/responsive/munin-dynazoom.tmpl | 82 ++++++++++ templates/responsive/munin-nodeview.tmpl | 49 ++++++ templates/responsive/munin-overview.tmpl | 68 +++++++++ templates/responsive/munin-problemview.tmpl | 141 ++++++++++++++++++ templates/responsive/munin-serviceview.tmpl | 105 +++++++++++++ .../responsive/partial/bottom_navigation.tmpl | 33 ++++ templates/responsive/partial/footer.tmpl | 7 + .../responsive/partial/generated_by.tmpl | 5 + templates/responsive/partial/head.tmpl | 40 +++++ .../responsive/partial/logo_navigation.tmpl | 38 +++++ .../partial/logo_navigation_comparison.tmpl | 33 ++++ .../partial/logo_navigation_problem.tmpl | 12 ++ templates/responsive/partial/logo_path.tmpl | 7 + templates/responsive/partial/navigation.tmpl | 30 ++++ templates/responsive/partial/path.tmpl | 1 + 21 files changed, 950 insertions(+) create mode 100644 templates/responsive/munin-categoryview.tmpl create mode 100644 templates/responsive/munin-comparison-day.tmpl create mode 100644 templates/responsive/munin-comparison-month.tmpl create mode 100644 templates/responsive/munin-comparison-week.tmpl create mode 100644 templates/responsive/munin-comparison-year.tmpl create mode 100644 templates/responsive/munin-domainview.tmpl create mode 100644 templates/responsive/munin-dynazoom.tmpl create mode 100644 templates/responsive/munin-nodeview.tmpl create mode 100644 templates/responsive/munin-overview.tmpl create mode 100644 templates/responsive/munin-problemview.tmpl create mode 100644 templates/responsive/munin-serviceview.tmpl create mode 100644 templates/responsive/partial/bottom_navigation.tmpl create mode 100644 templates/responsive/partial/footer.tmpl create mode 100644 templates/responsive/partial/generated_by.tmpl create mode 100644 templates/responsive/partial/head.tmpl create mode 100644 templates/responsive/partial/logo_navigation.tmpl create mode 100644 templates/responsive/partial/logo_navigation_comparison.tmpl create mode 100644 templates/responsive/partial/logo_navigation_problem.tmpl create mode 100644 templates/responsive/partial/logo_path.tmpl create mode 100644 templates/responsive/partial/navigation.tmpl create mode 100644 templates/responsive/partial/path.tmpl diff --git a/templates/responsive/munin-categoryview.tmpl b/templates/responsive/munin-categoryview.tmpl new file mode 100644 index 00000000..69455d08 --- /dev/null +++ b/templates/responsive/munin-categoryview.tmpl @@ -0,0 +1,55 @@ + + + +

+ + + diff --git a/templates/responsive/munin-comparison-day.tmpl b/templates/responsive/munin-comparison-day.tmpl new file mode 100644 index 00000000..d31158a2 --- /dev/null +++ b/templates/responsive/munin-comparison-day.tmpl @@ -0,0 +1,44 @@ + + + + + + + diff --git a/templates/responsive/munin-comparison-month.tmpl b/templates/responsive/munin-comparison-month.tmpl new file mode 100644 index 00000000..f02ae28a --- /dev/null +++ b/templates/responsive/munin-comparison-month.tmpl @@ -0,0 +1,42 @@ + + + + + + + diff --git a/templates/responsive/munin-comparison-week.tmpl b/templates/responsive/munin-comparison-week.tmpl new file mode 100644 index 00000000..03d924a7 --- /dev/null +++ b/templates/responsive/munin-comparison-week.tmpl @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/templates/responsive/munin-comparison-year.tmpl b/templates/responsive/munin-comparison-year.tmpl new file mode 100644 index 00000000..c2eadbe5 --- /dev/null +++ b/templates/responsive/munin-comparison-year.tmpl @@ -0,0 +1,42 @@ + + + + + + + diff --git a/templates/responsive/munin-domainview.tmpl b/templates/responsive/munin-domainview.tmpl new file mode 100644 index 00000000..5afe139e --- /dev/null +++ b/templates/responsive/munin-domainview.tmpl @@ -0,0 +1,72 @@ + + + + +
+ +
+

Domain view

+ +
+
+
+ + + diff --git a/templates/responsive/munin-dynazoom.tmpl b/templates/responsive/munin-dynazoom.tmpl new file mode 100644 index 00000000..9622372e --- /dev/null +++ b/templates/responsive/munin-dynazoom.tmpl @@ -0,0 +1,82 @@ + + + + +
+ +
+

Graph zoom

+ +
+ +
+ +
+ + + + +
+ + + Zooming is easy, it's done in 3 clicks (regular clicks, no drag&drop): +
    +
  1. First click to define the start of zoom. +
  2. Second click to define the ending of zoom. +
  3. Third click inside the defined zone to zoom, outside to cancel the zone. +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Plugin Name (domain/hostname/plugin_name) :
Start/Stop of the graph
(format:2005-08-15T15:52:01+0000)
(epoch) :
+ /
+ + ( / ) +
Limit low/high : + + / +
Graph size (w/o legend) (pixels): + / +
+ + +
+ + + + + +
+ + + diff --git a/templates/responsive/munin-nodeview.tmpl b/templates/responsive/munin-nodeview.tmpl new file mode 100644 index 00000000..a1b5f5ba --- /dev/null +++ b/templates/responsive/munin-nodeview.tmpl @@ -0,0 +1,49 @@ + + + + + + + diff --git a/templates/responsive/munin-overview.tmpl b/templates/responsive/munin-overview.tmpl new file mode 100644 index 00000000..fc446866 --- /dev/null +++ b/templates/responsive/munin-overview.tmpl @@ -0,0 +1,68 @@ + + + + + + + diff --git a/templates/responsive/munin-problemview.tmpl b/templates/responsive/munin-problemview.tmpl new file mode 100644 index 00000000..fd25afc1 --- /dev/null +++ b/templates/responsive/munin-problemview.tmpl @@ -0,0 +1,141 @@ + + + + + + + diff --git a/templates/responsive/munin-serviceview.tmpl b/templates/responsive/munin-serviceview.tmpl new file mode 100644 index 00000000..a89d1375 --- /dev/null +++ b/templates/responsive/munin-serviceview.tmpl @@ -0,0 +1,105 @@ + + + +
+ +
+

Service graphs

+ + + + + + + + + + + + + + + + + +
"> + " + alt="daily graph" + class=iwarncrit + width="" + height=""/> + "> + " + alt="weekly graph" + class=iwarncrit + width="" + height=""/> +
"> + " + alt="monthly graph" + class=iwarncrit + width="" + height=""/> + "> + " + alt="yearly graph" + class=iwarncrit + width="" + height=""/> +
" + alt="summed weekly graph" + width="" + height=""/> + " + alt="summed weekly graph" + width="" + height=""/> +
+

Graph Information

+ + +

+
+

Note: +This service is in WARNING state because one of the values reported is outside the allowed range. Please see further down for information about the ranges and the graph for the values. +

+

Note: +This service is in CRITICAL state because one of the values reported is outside the allowed range. Please see further down for information about the ranges and the graph for the values. +

+

Note: This service is in UNKNOWN state, because something bad happened. Please check your network, the munin-node, and the plugin. +

+ + + + + + + + + + + +oddrowevenrow lastrow"> + + + + + + + + +oddrowevenrow"> + + + +
FieldInternal nameTypeWarnCritInfo
 
 
 
+ This field has the following extra information: +
+ +
+
+
+ + + + diff --git a/templates/responsive/partial/bottom_navigation.tmpl b/templates/responsive/partial/bottom_navigation.tmpl new file mode 100644 index 00000000..3e0f047c --- /dev/null +++ b/templates/responsive/partial/bottom_navigation.tmpl @@ -0,0 +1,33 @@ + + + :: + "> + + + + "> + Overview + + + + + + :: + + :: + + :: "> + + diff --git a/templates/responsive/partial/footer.tmpl b/templates/responsive/partial/footer.tmpl new file mode 100644 index 00000000..54b28696 --- /dev/null +++ b/templates/responsive/partial/footer.tmpl @@ -0,0 +1,7 @@ + + diff --git a/templates/responsive/partial/generated_by.tmpl b/templates/responsive/partial/generated_by.tmpl new file mode 100644 index 00000000..e03f9d05 --- /dev/null +++ b/templates/responsive/partial/generated_by.tmpl @@ -0,0 +1,5 @@ + + + + +
diff --git a/templates/responsive/partial/head.tmpl b/templates/responsive/partial/head.tmpl new file mode 100644 index 00000000..66fcb544 --- /dev/null +++ b/templates/responsive/partial/head.tmpl @@ -0,0 +1,40 @@ + + + + + /static/style-new.css" type="text/css" /> + + @@ -228,5 +229,5 @@ }); - - + + \ No newline at end of file diff --git a/templates/munstrap/templates/partial/footer.tmpl b/templates/munstrap/templates/partial/footer.tmpl index 62263e48..1b0d120c 100644 --- a/templates/munstrap/templates/partial/footer.tmpl +++ b/templates/munstrap/templates/partial/footer.tmpl @@ -1,28 +1,3 @@ -
-
-
- This page was generated by Munin version at with MunStrap template. -
-
- - - - - + \ No newline at end of file diff --git a/templates/munstrap/templates/partial/footer_pre.tmpl b/templates/munstrap/templates/partial/footer_pre.tmpl new file mode 100644 index 00000000..ec96ecca --- /dev/null +++ b/templates/munstrap/templates/partial/footer_pre.tmpl @@ -0,0 +1,16 @@ +
+ +
+
+ + This page was generated by Munin + version at + with MunStrap template. + +
+
+ + + + + diff --git a/templates/munstrap/templates/partial/head.tmpl b/templates/munstrap/templates/partial/head.tmpl index 9250da4f..9b40068b 100644 --- a/templates/munstrap/templates/partial/head.tmpl +++ b/templates/munstrap/templates/partial/head.tmpl @@ -2,7 +2,7 @@ Munin <TMPL_LOOP NAME="PATH"><TMPL_IF NAME="pathname"> :: <TMPL_VAR ESCAPE="HTML" NAME="pathname"><TMPL_ELSE>Munin</TMPL_IF></TMPL_LOOP> - + @@ -13,9 +13,8 @@ /static/img/favicon.png" /> /static/css/bootstrap.min.css" rel="stylesheet" /> /static/css/style-munstrap.css" rel="stylesheet" /> - - + - -
- + +
- diff --git a/templates/munstrap/templates/partial/path.tmpl b/templates/munstrap/templates/partial/path.tmpl index fb27324c..49fa8630 100644 --- a/templates/munstrap/templates/partial/path.tmpl +++ b/templates/munstrap/templates/partial/path.tmpl @@ -1 +1,19 @@ - :: ">">Overview + + :: + + "> + + + + + + + + "> + + Overview + + + + + From 293b5cb41062e04c0306b33f112b791a4134de02 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 05:59:38 +0100 Subject: [PATCH 309/718] Fix some errors --- templates/munstrap/static/js/munstrap.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/munstrap/static/js/munstrap.js b/templates/munstrap/static/js/munstrap.js index ae56b863..84739474 100644 --- a/templates/munstrap/static/js/munstrap.js +++ b/templates/munstrap/static/js/munstrap.js @@ -2,7 +2,7 @@ * Sanitize all tab links */ $( "ul#tabs>li>a" ).each(function( index ) { - var eid = element_clear($( this ).attr('href')).replace(/[^#\w]/gi,'_'); + var eid = $( this ).attr('href').replace(/[^#\w]/gi,'_'); $( this ).attr('href', eid); }); @@ -10,7 +10,7 @@ $( "ul#tabs>li>a" ).each(function( index ) { * Sanitize all tab ids */ $( "div#munin_nodeview_tab>div" ).each(function( index ) { - var eid = element_clear($( this ).attr('id')).replace(/[^\w]/gi,'_'); + var eid = $( this ).attr('id').replace(/[^\w]/gi,'_'); $( this ).attr('id', eid); }); @@ -27,5 +27,5 @@ $(document).ready(function() { }); $(window).on('popstate', function() { var anchor = location.hash || $("a[data-toggle=tab]").first().attr("href"); - $('a[href="' + location.hash + '"]').tab('show'); + $('a[href="' + anchor + '"]').tab('show'); }); \ No newline at end of file From e23d6f9ab0f745d2a70bf67d73233d8982e97dce Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 06:09:09 +0100 Subject: [PATCH 310/718] Reformatted code, minimize css and optimize css --- .../munstrap/static/css/style-munstrap.css | 66 ++++++++++--------- .../static/css/style-munstrap.min.css | 1 + 2 files changed, 36 insertions(+), 31 deletions(-) create mode 100644 templates/munstrap/static/css/style-munstrap.min.css diff --git a/templates/munstrap/static/css/style-munstrap.css b/templates/munstrap/static/css/style-munstrap.css index 39355906..2f633db5 100644 --- a/templates/munstrap/static/css/style-munstrap.css +++ b/templates/munstrap/static/css/style-munstrap.css @@ -1,53 +1,58 @@ -@media (min-width:992px){ - .modal-lg {width:940px !important;} +@media (min-width: 992px) { + .modal-lg { + width: 940px !important; + } } -body{ - padding-top: 70px; +body { + padding-top: 70px; } img.i { - display: block; - margin: 10px auto; + display: block; + margin: 10px auto; } -img.img-zoom{ - cursor: pointer; +img.img-zoom { + cursor: pointer; } -div.service-alert{ - margin-top: 10px; +div.service-alert { + margin-top: 10px; } -img#zoom_image{ - margin-bottom: 15px; +img#zoom_image { + margin-bottom: 15px; } .link-domain { - font-size: 1.4em; - color: #660066; + font-size: 1.4em; + color: #660066; } + .link-host { - font-size: 1.1em; - color: #800080; + font-size: 1.1em; + color: #800080; } + ul.groupview, ul.groupview ul { - list-style-type: none; + list-style-type: none; } + .munin-icon { - background: url(../img/logo-munin.png) left top; - margin-top: -6px; - width: 35px; - height: 35px; - display: block; - float: left; + background: url(../img/logo-munin.png) left top; + margin-top: -6px; + width: 35px; + height: 35px; + display: block; + float: left; } .dropdown-submenu { position: relative; } -.dropdown-submenu>.dropdown-menu { +.dropdown-submenu > .dropdown-menu { top: 0; left: 100%; margin-top: -6px; @@ -57,25 +62,24 @@ ul.groupview, ul.groupview ul { border-radius: 0 6px 6px 6px; } -.dropdown-submenu:hover>.dropdown-menu { +.dropdown-submenu:hover > .dropdown-menu { display: block; } -.dropdown-submenu>a:after { +.dropdown-submenu > a:after { display: block; content: " "; float: right; width: 0; height: 0; - border-color: transparent; - border-style: solid; - border-width: 5px 0 5px 5px; + border: 5px solid transparent; + border-right-width: 0; border-left-color: #ccc; margin-top: 5px; margin-right: -10px; } -.dropdown-submenu:hover>a:after { +.dropdown-submenu:hover > a:after { border-left-color: #fff; } @@ -83,7 +87,7 @@ ul.groupview, ul.groupview ul { float: none; } -.dropdown-submenu.pull-left>.dropdown-menu { +.dropdown-submenu.pull-left > .dropdown-menu { left: -100%; margin-left: 10px; -webkit-border-radius: 6px 0 6px 6px; diff --git a/templates/munstrap/static/css/style-munstrap.min.css b/templates/munstrap/static/css/style-munstrap.min.css new file mode 100644 index 00000000..b3d897ec --- /dev/null +++ b/templates/munstrap/static/css/style-munstrap.min.css @@ -0,0 +1 @@ +@media(min-width:992px){.modal-lg{width:940px !important}}body{padding-top:70px}img.i{display:block;margin:10px auto}img.img-zoom{cursor:pointer}div.service-alert{margin-top:10px}img#zoom_image{margin-bottom:15px}.link-domain{font-size:1.4em;color:#606}.link-host{font-size:1.1em;color:purple}ul.groupview,ul.groupview ul{list-style-type:none}.munin-icon{background:url(../img/logo-munin.png) left top;margin-top:-6px;width:35px;height:35px;display:block;float:left}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border:5px solid transparent;border-right-width:0;border-left-color:#ccc;margin-top:5px;margin-right:-10px}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px} \ No newline at end of file From 3533615be4c6a149571e129487487cc0e8edddf1 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 06:09:45 +0100 Subject: [PATCH 311/718] Use new minimized CSS --- templates/munstrap/templates/partial/head.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/munstrap/templates/partial/head.tmpl b/templates/munstrap/templates/partial/head.tmpl index 9b40068b..0cefcf46 100644 --- a/templates/munstrap/templates/partial/head.tmpl +++ b/templates/munstrap/templates/partial/head.tmpl @@ -12,7 +12,7 @@ /static/img/favicon.ico" /> /static/img/favicon.png" /> /static/css/bootstrap.min.css" rel="stylesheet" /> - /static/css/style-munstrap.css" rel="stylesheet" /> + /static/css/style-munstrap.min.css" rel="stylesheet" /> From 23f940fede6632ba66c4f85e205b96dd686a94f2 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 06:23:25 +0100 Subject: [PATCH 312/718] Generated minified JS and some formatting --- templates/munstrap/static/js/dynazoom.js | 363 +++++++++--------- templates/munstrap/static/js/dynazoom.min.js | 6 + .../munstrap/static/js/formatdate.min.js | 16 + templates/munstrap/static/js/munstrap.js | 20 +- templates/munstrap/static/js/munstrap.min.js | 2 + .../munstrap/static/js/querystring.min.js | 1 + 6 files changed, 217 insertions(+), 191 deletions(-) create mode 100644 templates/munstrap/static/js/dynazoom.min.js create mode 100644 templates/munstrap/static/js/formatdate.min.js create mode 100644 templates/munstrap/static/js/munstrap.min.js create mode 100644 templates/munstrap/static/js/querystring.min.js diff --git a/templates/munstrap/static/js/dynazoom.js b/templates/munstrap/static/js/dynazoom.js index 53ed91fa..aa662261 100644 --- a/templates/munstrap/static/js/dynazoom.js +++ b/templates/munstrap/static/js/dynazoom.js @@ -1,198 +1,199 @@ function refreshZoom(query, form, image, divOverlay) { - //INIT - var qs = new Querystring(query); - init(); - - var scale = refreshImg(); - var start_epoch = (+qs.get("rst_start_epoch", form.start_epoch.value)); - var stop_epoch = (+qs.get("rst_stop_epoch", form.stop_epoch.value)); - var initial_left; - var initial_top; - - //form.btnMaj.onclick = majDates; - form.plugin_name.onblur = refreshImg; - form.start_iso8601.onblur = majDates; - form.stop_iso8601.onblur = majDates; - form.start_epoch.onblur = refreshImg; - form.stop_epoch.onblur = refreshImg; - form.lower_limit.onblur = refreshImg; - form.upper_limit.onblur = refreshImg; - form.size_x.onblur = refreshImg; - form.size_y.onblur = refreshImg; - form.btnReset.onclick = reset; - - // Sets the onClick handler - image.onclick = click; - var clickCounter = 0; - - //FUNCTIONS - function init(){ - form.plugin_name.value = qs.get("plugin_name", "localdomain/localhost.localdomain/if_eth0"); - form.start_epoch.value = qs.get("start_epoch", "1236561663"); - form.stop_epoch.value = qs.get("stop_epoch", "1237561663"); - form.lower_limit.value = qs.get("lower_limit", ""); - form.upper_limit.value = qs.get("upper_limit", ""); - form.size_x.value = qs.get("size_x", ""); - form.size_y.value = qs.get("size_y", ""); - - updateStartStop(); - } - - function reset(event){ + //INIT + var qs = new Querystring(query); init(); - - //Can be not the initial ones in case of manual refresh - form.start_epoch.value = start_epoch; - form.stop_epoch.value = stop_epoch; - updateStartStop(); - - //Redraw - scale = refreshImg(); - - //Reset gui - clickCounter = 0; - initial_left = 0; - initial_top = 0; - - image.onmousemove = undefined; - form.start_iso8601.disabled = false; - form.stop_iso8601.disabled = false; - form.start_epoch.disabled = false; - form.stop_epoch.disabled = false; - } - function refreshImg(event) { - image.src = qs.get("cgiurl_graph", "/munin-cgi/munin-cgi-graph") + "/" - + form.plugin_name.value - + "-pinpoint=" + parseInt(form.start_epoch.value) + "," + parseInt(form.stop_epoch.value) - + ".png" - + "?" - + "&lower_limit=" + form.lower_limit.value - + "&upper_limit=" + form.upper_limit.value - + "&size_x=" + form.size_x.value - + "&size_y=" + form.size_y.value - ; + var scale = refreshImg(); + var start_epoch = (+qs.get("rst_start_epoch", form.start_epoch.value)); + var stop_epoch = (+qs.get("rst_stop_epoch", form.stop_epoch.value)); + var initial_left; + var initial_top; - return ((+form.stop_epoch.value) - (+form.start_epoch.value)) / (+form.size_x.value); - } + //form.btnMaj.onclick = majDates; + form.plugin_name.onblur = refreshImg; + form.start_iso8601.onblur = majDates; + form.stop_iso8601.onblur = majDates; + form.start_epoch.onblur = refreshImg; + form.stop_epoch.onblur = refreshImg; + form.lower_limit.onblur = refreshImg; + form.upper_limit.onblur = refreshImg; + form.size_x.onblur = refreshImg; + form.size_y.onblur = refreshImg; + form.btnReset.onclick = reset; - function updateStartStop() { - form.start_iso8601.value = new Date(form.start_epoch.value * 1000).formatDate(Date.DATE_ISO8601); - form.stop_iso8601.value = new Date(form.stop_epoch.value * 1000).formatDate(Date.DATE_ISO8601); - } + // Sets the onClick handler + image.onclick = click; + var clickCounter = 0; - function divMouseMove(event) { - var delta_x; - var size_x; + //FUNCTIONS + function init() { + form.plugin_name.value = qs.get("plugin_name", "localdomain/localhost.localdomain/if_eth0"); + form.start_epoch.value = qs.get("start_epoch", "1236561663"); + form.stop_epoch.value = qs.get("stop_epoch", "1237561663"); + form.lower_limit.value = qs.get("lower_limit", ""); + form.upper_limit.value = qs.get("upper_limit", ""); + form.size_x.value = qs.get("size_x", ""); + form.size_y.value = qs.get("size_y", ""); - // Handling the borders (X1>X2 ou X1X2 ou X1a&&(a+=1E3);1==String(a).length&&(a="00"+a);2==String(a).length&&(a="0"+a);return a},c:function(){return this.Y()+"-"+this.m()+"-"+this.d()+"T"+this.H()+":"+this.i()+":"+this.s()+this.P()},d:function(){var a=String(this.j());return 1==a.length?"0"+a:a},D:function(){return m[b.getDay()]},F:function(){return p[b.getMonth()]},g:function(){return 0==b.getHours()?12:12b.getTimezoneOffset()?"+"+e+a:"-"+e+a},P:function(){var a=this.O();return a.substr(0,3)+":"+a.substr(3,2)},r:function(){return this.D()+", "+this.d()+" "+this.M()+" "+this.Y()+" "+this.H()+":"+ +this.i()+":"+this.s()+" "+this.O()},s:function(){var a=String(b.getSeconds());return 1==a.length?"0"+a:a},S:function(){switch(b.getDate()){case 1:return"st";case 2:return"nd";case 3:return"rd";case 21:return"st";case 22:return"nd";case 23:return"rd";case 31:return"st";default:return"th"}},t:function(){return 1==this.L()&&2==this.n()?29:[null,31,28,31,30,31,30,31,31,30,31,30,31][this.n()]},U:function(){return Math.round(b.getTime()/1E3)},w:function(){return b.getDay()},W:function(){var a=this.N(), +b=this.z(),c=364+this.L()-b;if(2>=c&&a<=3-c)return 1;if(2>=b&&5<=a)return(new Date(this.Y()-1,11,31)).formatDate("W");a=(new Date(this.Y(),0,1)).getDay();a=0!=a?a-1:6;return 3>=a?1+Math.floor((b+a)/7):1+Math.floor((b-(7-a))/7)},y:function(){var a=String(this.Y());return a.substring(a.length-2,a.length)},Y:function(){if(b.getFullYear){var a=(new Date("January 1 2001 00:00:00 +0000")).getFullYear();if(2001==a)return b.getFullYear()}a=b.getYear();a%=100;return a+(38>a?2E3:1900)},z:function(){var a="January 1 "+ +this.Y()+" 00:00:00 GMT"+this.O(),a=new Date(a),a=b.getTime()-a.getTime();return Math.floor(a/1E3/60/60/24)},Z:function(){return-60*b.getTimezoneOffset()}},b;b=f?new Date(f):this;for(var c=h.split(""),d=0;dli>a" ).each(function( index ) { - var eid = $( this ).attr('href').replace(/[^#\w]/gi,'_'); - $( this ).attr('href', eid); +$("ul#tabs>li>a").each(function (index) { + var eid = $(this).attr('href').replace(/[^#\w]/gi, '_'); + $(this).attr('href', eid); }); /* * Sanitize all tab ids */ -$( "div#munin_nodeview_tab>div" ).each(function( index ) { - var eid = $( this ).attr('id').replace(/[^\w]/gi,'_'); - $( this ).attr('id', eid); +$("div#munin_nodeview_tab>div").each(function (index) { + var eid = $(this).attr('id').replace(/[^\w]/gi, '_'); + $(this).attr('id', eid); }); /* * Update the URL with selected tab and active selected tab on page refresh */ -$(document).ready(function() { - if(location.hash) { +$(document).ready(function () { + if (location.hash) { $('a[href="' + location.hash + '"]').tab('show'); } - $(document.body).on("click", "a[data-toggle=tab]", function(event) { + $(document.body).on("click", "a[data-toggle=tab]", function (event) { location.hash = this.getAttribute("href"); }); }); -$(window).on('popstate', function() { +$(window).on('popstate', function () { var anchor = location.hash || $("a[data-toggle=tab]").first().attr("href"); $('a[href="' + anchor + '"]').tab('show'); }); \ No newline at end of file diff --git a/templates/munstrap/static/js/munstrap.min.js b/templates/munstrap/static/js/munstrap.min.js new file mode 100644 index 00000000..142b24ff --- /dev/null +++ b/templates/munstrap/static/js/munstrap.min.js @@ -0,0 +1,2 @@ +$("ul#tabs>li>a").each(function(a){a=$(this).attr("href").replace(/[^#\w]/gi,"_");$(this).attr("href",a)});$("div#munin_nodeview_tab>div").each(function(a){a=$(this).attr("id").replace(/[^\w]/gi,"_");$(this).attr("id",a)});$(document).ready(function(){location.hash&&$('a[href="'+location.hash+'"]').tab("show");$(document.body).on("click","a[data-toggle=tab]",function(a){location.hash=this.getAttribute("href")})}); +$(window).on("popstate",function(){var a=location.hash||$("a[data-toggle=tab]").first().attr("href");$('a[href="'+a+'"]').tab("show")}); diff --git a/templates/munstrap/static/js/querystring.min.js b/templates/munstrap/static/js/querystring.min.js new file mode 100644 index 00000000..e914a94b --- /dev/null +++ b/templates/munstrap/static/js/querystring.min.js @@ -0,0 +1 @@ +function Querystring(a){this.params={};var b=window.location.search;null==a&&(a=b.substring(1,b.length));if(0!=a.length)for(a=a.replace(/\+/g," "),a=a.split("&"),b=0;b Date: Wed, 7 Dec 2016 06:23:54 +0100 Subject: [PATCH 313/718] Moved generated by to the correct file and use min js --- .../munstrap/templates/partial/footer_pre.tmpl | 12 ++---------- .../munstrap/templates/partial/generated_by.tmpl | 14 +++++++++----- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/templates/munstrap/templates/partial/footer_pre.tmpl b/templates/munstrap/templates/partial/footer_pre.tmpl index ec96ecca..3527cfba 100644 --- a/templates/munstrap/templates/partial/footer_pre.tmpl +++ b/templates/munstrap/templates/partial/footer_pre.tmpl @@ -1,16 +1,8 @@
-
-
- - This page was generated by Munin - version at - with MunStrap template. - -
-
+ - + diff --git a/templates/munstrap/templates/partial/generated_by.tmpl b/templates/munstrap/templates/partial/generated_by.tmpl index cef2100b..e9428ac1 100644 --- a/templates/munstrap/templates/partial/generated_by.tmpl +++ b/templates/munstrap/templates/partial/generated_by.tmpl @@ -1,5 +1,9 @@ - - - - -
+
+
+ + This page was generated by Munin + version at + with MunStrap template. + +
+
\ No newline at end of file From 1cc4d0f2df40ddd5acc87af3fdc6df787ff12ac4 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 06:24:09 +0100 Subject: [PATCH 314/718] Use minified JS --- templates/munstrap/templates/munin-serviceview.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/munstrap/templates/munin-serviceview.tmpl b/templates/munstrap/templates/munin-serviceview.tmpl index 8bc972a6..75231167 100644 --- a/templates/munstrap/templates/munin-serviceview.tmpl +++ b/templates/munstrap/templates/munin-serviceview.tmpl @@ -197,9 +197,10 @@
- - - + + + + \ No newline at end of file From 3c7693854e971a3be77b3ead0f2397e7353b1907 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 06:45:25 +0100 Subject: [PATCH 315/718] Added path to include --- templates/munstrap/templates/partial/footer_pre.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/munstrap/templates/partial/footer_pre.tmpl b/templates/munstrap/templates/partial/footer_pre.tmpl index 3527cfba..20f8ffef 100644 --- a/templates/munstrap/templates/partial/footer_pre.tmpl +++ b/templates/munstrap/templates/partial/footer_pre.tmpl @@ -1,6 +1,6 @@ - + From 4d7b1b5f03c340bf78dbac398b3230f7c45c2382 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 06:51:11 +0100 Subject: [PATCH 316/718] Undo previous commit --- templates/munstrap/templates/partial/footer_pre.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/munstrap/templates/partial/footer_pre.tmpl b/templates/munstrap/templates/partial/footer_pre.tmpl index 20f8ffef..3527cfba 100644 --- a/templates/munstrap/templates/partial/footer_pre.tmpl +++ b/templates/munstrap/templates/partial/footer_pre.tmpl @@ -1,6 +1,6 @@ - + From 4d4578143eb96c008349ebe0a3d55861d6791e11 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 07:46:55 +0100 Subject: [PATCH 317/718] Removed include due an perl error, need more work --- .../munstrap/templates/partial/footer_pre.tmpl | 10 +++++++++- .../munstrap/templates/partial/generated_by.tmpl | 14 +++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/templates/munstrap/templates/partial/footer_pre.tmpl b/templates/munstrap/templates/partial/footer_pre.tmpl index 3527cfba..406d5aa5 100644 --- a/templates/munstrap/templates/partial/footer_pre.tmpl +++ b/templates/munstrap/templates/partial/footer_pre.tmpl @@ -1,6 +1,14 @@ - +
+
+ + This page was generated by Munin + version at + with MunStrap template. + +
+
diff --git a/templates/munstrap/templates/partial/generated_by.tmpl b/templates/munstrap/templates/partial/generated_by.tmpl index e9428ac1..2755029a 100644 --- a/templates/munstrap/templates/partial/generated_by.tmpl +++ b/templates/munstrap/templates/partial/generated_by.tmpl @@ -1,9 +1,5 @@ -
-
- - This page was generated by Munin - version at - with MunStrap template. - -
-
\ No newline at end of file + + + + +
\ No newline at end of file From 5f38fb4015080df6d9ace2203a111960967099f3 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Wed, 7 Dec 2016 08:39:52 +0100 Subject: [PATCH 318/718] Use UglyJS instead Google Closure Compiler --- templates/munstrap/static/js/dynazoom.min.js | 7 +------ templates/munstrap/static/js/formatdate.min.js | 17 +---------------- templates/munstrap/static/js/querystring.min.js | 2 +- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/templates/munstrap/static/js/dynazoom.min.js b/templates/munstrap/static/js/dynazoom.min.js index fd75aebe..a268cedd 100644 --- a/templates/munstrap/static/js/dynazoom.min.js +++ b/templates/munstrap/static/js/dynazoom.min.js @@ -1,6 +1 @@ -function refreshZoom(t,a,g,w){function m(){a.plugin_name.value=c.get("plugin_name","localdomain/localhost.localdomain/if_eth0");a.start_epoch.value=c.get("start_epoch","1236561663");a.stop_epoch.value=c.get("stop_epoch","1237561663");a.lower_limit.value=c.get("lower_limit","");a.upper_limit.value=c.get("upper_limit","");a.size_x.value=c.get("size_x","");a.size_y.value=c.get("size_y","");h()}function e(b){g.src=c.get("cgiurl_graph","/munin-cgi/munin-cgi-graph")+"/"+a.plugin_name.value+"-pinpoint="+ -parseInt(a.start_epoch.value)+","+parseInt(a.stop_epoch.value)+".png?&lower_limit="+a.lower_limit.value+"&upper_limit="+a.upper_limit.value+"&size_x="+a.size_x.value+"&size_y="+a.size_y.value;return(+a.stop_epoch.value-+a.start_epoch.value)/+a.size_x.value}function h(){a.start_iso8601.value=(new Date(1E3*a.start_epoch.value)).formatDate(Date.DATE_ISO8601);a.stop_iso8601.value=(new Date(1E3*a.stop_epoch.value)).formatDate(Date.DATE_ISO8601)}function u(b){var d;b=n(b)[0];d=b-f;0>d?(b-=63,d=-d):b=f- -63;a.start_epoch.value=k+l*b;a.stop_epoch.value=k+l*(b+d);h()}function p(a,d){return a+d.substring(a.length,d.length)}function q(b){var d=p(a.start_iso8601.value,"2009-01-01T00:00:00+0100");b=p(a.stop_iso8601.value,"2009-01-01T00:00:00+0100");var c=/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).(\d{4})/;c.test(d)&&(d=new Date(d.replace(c,"$2 $3, $1 $4:$5:$6")),a.start_epoch.value=d.getTime()/1E3);c.test(b)&&(d=new Date(b.replace(c,"$2 $3, $1 $4:$5:$6")),a.stop_epoch.value=d.getTime()/1E3);e()}function n(a){var d= -0,c=0,b;b=g;if("undefined"!=typeof b.offsetParent){for(var e=0,f=0;b;b=b.offsetParent)e+=b.offsetLeft,f+=b.offsetTop;b=[e,f]}else b=[b.x,b.y];a||(a=window.event);if(a.pageX||a.pageY)d=a.pageX,c=a.pageY;else if(a.clientX||a.clientY)d=a.clientX+document.body.scrollLeft+document.documentElement.scrollLeft,c=a.clientY+document.body.scrollTop+document.documentElement.scrollTop;d-=b[0];c-=b[1];return[d,c]}var c=new Querystring(t);m();var l=e(),k=+c.get("rst_start_epoch",a.start_epoch.value),v=+c.get("rst_stop_epoch", -a.stop_epoch.value),f;a.plugin_name.onblur=e;a.start_iso8601.onblur=q;a.stop_iso8601.onblur=q;a.start_epoch.onblur=e;a.stop_epoch.onblur=e;a.lower_limit.onblur=e;a.upper_limit.onblur=e;a.size_x.onblur=e;a.size_y.onblur=e;a.btnReset.onclick=function(b){m();a.start_epoch.value=k;a.stop_epoch.value=v;h();l=e();f=r=0;g.onmousemove=void 0;a.start_iso8601.disabled=!1;a.stop_iso8601.disabled=!1;a.start_epoch.disabled=!1;a.stop_epoch.disabled=!1};g.onclick=function(b){switch(r++%2){case 0:f=n(b)[0];a.start_iso8601.disabled= -!0;a.stop_iso8601.disabled=!0;a.start_epoch.disabled=!0;a.stop_epoch.disabled=!0;g.onmousemove=u;break;case 1:g.onmousemove=void 0,a.start_iso8601.disabled=!1,a.stop_iso8601.disabled=!1,a.start_epoch.disabled=!1,a.stop_epoch.disabled=!1}};var r=0}; +function refreshZoom(query,form,image,divOverlay){var qs=new Querystring(query);init();var scale=refreshImg();var start_epoch=+qs.get("rst_start_epoch",form.start_epoch.value);var stop_epoch=+qs.get("rst_stop_epoch",form.stop_epoch.value);var initial_left;var initial_top;form.plugin_name.onblur=refreshImg;form.start_iso8601.onblur=majDates;form.stop_iso8601.onblur=majDates;form.start_epoch.onblur=refreshImg;form.stop_epoch.onblur=refreshImg;form.lower_limit.onblur=refreshImg;form.upper_limit.onblur=refreshImg;form.size_x.onblur=refreshImg;form.size_y.onblur=refreshImg;form.btnReset.onclick=reset;image.onclick=click;var clickCounter=0;function init(){form.plugin_name.value=qs.get("plugin_name","localdomain/localhost.localdomain/if_eth0");form.start_epoch.value=qs.get("start_epoch","1236561663");form.stop_epoch.value=qs.get("stop_epoch","1237561663");form.lower_limit.value=qs.get("lower_limit","");form.upper_limit.value=qs.get("upper_limit","");form.size_x.value=qs.get("size_x","");form.size_y.value=qs.get("size_y","");updateStartStop()}function reset(event){init();form.start_epoch.value=start_epoch;form.stop_epoch.value=stop_epoch;updateStartStop();scale=refreshImg();clickCounter=0;initial_left=0;initial_top=0;image.onmousemove=undefined;form.start_iso8601.disabled=false;form.stop_iso8601.disabled=false;form.start_epoch.disabled=false;form.stop_epoch.disabled=false}function refreshImg(event){image.src=qs.get("cgiurl_graph","/munin-cgi/munin-cgi-graph")+"/"+form.plugin_name.value+"-pinpoint="+parseInt(form.start_epoch.value)+","+parseInt(form.stop_epoch.value)+".png"+"?"+"&lower_limit="+form.lower_limit.value+"&upper_limit="+form.upper_limit.value+"&size_x="+form.size_x.value+"&size_y="+form.size_y.value;return(+form.stop_epoch.value-+form.start_epoch.value)/+form.size_x.value}function updateStartStop(){form.start_iso8601.value=new Date(form.start_epoch.value*1e3).formatDate(Date.DATE_ISO8601);form.stop_iso8601.value=new Date(form.stop_epoch.value*1e3).formatDate(Date.DATE_ISO8601)}function divMouseMove(event){var delta_x;var size_x;var posX=getCoordinatesOnImage(event)[0];var current_width=posX-initial_left;if(current_width<0){delta_x=posX-63;size_x=-current_width}else{delta_x=initial_left-63;size_x=current_width}form.start_epoch.value=start_epoch+scale*delta_x;form.stop_epoch.value=start_epoch+scale*(delta_x+size_x);updateStartStop()}function startZoom(event){var pos=getCoordinatesOnImage(event);initial_left=pos[0];initial_top=pos[1];form.start_iso8601.disabled=true;form.stop_iso8601.disabled=true;form.start_epoch.disabled=true;form.stop_epoch.disabled=true;image.onmousemove=divMouseMove}function endZoom(event){image.onmousemove=undefined;form.start_iso8601.disabled=false;form.stop_iso8601.disabled=false;form.start_epoch.disabled=false;form.stop_epoch.disabled=false}function fillDate(date,default_date){return date+default_date.substring(date.length,default_date.length)}function majDates(event){var default_date="2009-01-01T00:00:00+0100";var start_manual=fillDate(form.start_iso8601.value,default_date);var stop_manual=fillDate(form.stop_iso8601.value,default_date);var dateRegex=/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).(\d{4})/;if(dateRegex.test(start_manual)){var date_parsed=new Date(start_manual.replace(dateRegex,"$2 $3, $1 $4:$5:$6"));form.start_epoch.value=date_parsed.getTime()/1e3}if(dateRegex.test(stop_manual)){var date_parsed=new Date(stop_manual.replace(dateRegex,"$2 $3, $1 $4:$5:$6"));form.stop_epoch.value=date_parsed.getTime()/1e3}refreshImg()}function click(event){switch(clickCounter++%2){case 0:startZoom(event);break;case 1:endZoom(event);break}}function findPosition(oElement){if(typeof oElement.offsetParent!="undefined"){for(var posX=0,posY=0;oElement;oElement=oElement.offsetParent){posX+=oElement.offsetLeft;posY+=oElement.offsetTop}return[posX,posY]}else{return[oElement.x,oElement.y]}}function getCoordinatesOnImage(event){var posX=0;var posY=0;var imgPos;imgPos=findPosition(image);if(!event)var event=window.event;if(event.pageX||event.pageY){posX=event.pageX;posY=event.pageY}else if(event.clientX||event.clientY){posX=event.clientX+document.body.scrollLeft+document.documentElement.scrollLeft;posY=event.clientY+document.body.scrollTop+document.documentElement.scrollTop}posX=posX-imgPos[0];posY=posY-imgPos[1];return[posX,posY]}} \ No newline at end of file diff --git a/templates/munstrap/static/js/formatdate.min.js b/templates/munstrap/static/js/formatdate.min.js index 1fd0cf1a..1e52267f 100644 --- a/templates/munstrap/static/js/formatdate.min.js +++ b/templates/munstrap/static/js/formatdate.min.js @@ -1,16 +1 @@ -formatdate.js:187: WARNING - Suspicious code. This code lacks side-effects. Is there a bug? - h.length == 1? h = "0"+h:1; - ^ - -formatdate.js:188: WARNING - Suspicious code. This code lacks side-effects. Is there a bug? - m.length == 1? m = "0"+m:1; - ^ - -0 error(s), 2 warning(s) -Date.prototype.formatDate=function(h,f){function k(a){return void 0!=g[a]?g[a]():a}var l="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),m="Sun Mon Tue Wed Thu Fri Sat".split(" "),n="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),p="January February March April May June July August September October November December".split(" "),g={a:function(){return 11a&&(a+=1E3);1==String(a).length&&(a="00"+a);2==String(a).length&&(a="0"+a);return a},c:function(){return this.Y()+"-"+this.m()+"-"+this.d()+"T"+this.H()+":"+this.i()+":"+this.s()+this.P()},d:function(){var a=String(this.j());return 1==a.length?"0"+a:a},D:function(){return m[b.getDay()]},F:function(){return p[b.getMonth()]},g:function(){return 0==b.getHours()?12:12b.getTimezoneOffset()?"+"+e+a:"-"+e+a},P:function(){var a=this.O();return a.substr(0,3)+":"+a.substr(3,2)},r:function(){return this.D()+", "+this.d()+" "+this.M()+" "+this.Y()+" "+this.H()+":"+ -this.i()+":"+this.s()+" "+this.O()},s:function(){var a=String(b.getSeconds());return 1==a.length?"0"+a:a},S:function(){switch(b.getDate()){case 1:return"st";case 2:return"nd";case 3:return"rd";case 21:return"st";case 22:return"nd";case 23:return"rd";case 31:return"st";default:return"th"}},t:function(){return 1==this.L()&&2==this.n()?29:[null,31,28,31,30,31,30,31,31,30,31,30,31][this.n()]},U:function(){return Math.round(b.getTime()/1E3)},w:function(){return b.getDay()},W:function(){var a=this.N(), -b=this.z(),c=364+this.L()-b;if(2>=c&&a<=3-c)return 1;if(2>=b&&5<=a)return(new Date(this.Y()-1,11,31)).formatDate("W");a=(new Date(this.Y(),0,1)).getDay();a=0!=a?a-1:6;return 3>=a?1+Math.floor((b+a)/7):1+Math.floor((b-(7-a))/7)},y:function(){var a=String(this.Y());return a.substring(a.length-2,a.length)},Y:function(){if(b.getFullYear){var a=(new Date("January 1 2001 00:00:00 +0000")).getFullYear();if(2001==a)return b.getFullYear()}a=b.getYear();a%=100;return a+(38>a?2E3:1900)},z:function(){var a="January 1 "+ -this.Y()+" 00:00:00 GMT"+this.O(),a=new Date(a),a=b.getTime()-a.getTime();return Math.floor(a/1E3/60/60/24)},Z:function(){return-60*b.getTimezoneOffset()}},b;b=f?new Date(f):this;for(var c=h.split(""),d=0;d11?"pm":"am"},A:function(){return this.a().toUpperCase()},B:function(){var off=(date.getTimezoneOffset()+60)*60;var theSeconds=date.getHours()*3600+date.getMinutes()*60+date.getSeconds()+off;var beat=Math.floor(theSeconds/86.4);if(beat>1e3)beat-=1e3;if(beat<0)beat+=1e3;if(String(beat).length==1)beat="00"+beat;if(String(beat).length==2)beat="0"+beat;return beat},c:function(){return this.Y()+"-"+this.m()+"-"+this.d()+"T"+this.H()+":"+this.i()+":"+this.s()+this.P()},d:function(){var j=String(this.j());return j.length==1?"0"+j:j},D:function(){return daysShort[date.getDay()]},F:function(){return monthsLong[date.getMonth()]},g:function(){if(date.getHours()==0){return 12}else{return date.getHours()>12?date.getHours()-12:date.getHours()}},G:function(){return date.getHours()},h:function(){var g=String(this.g());return g.length==1?"0"+g:g},H:function(){var G=String(this.G());return G.length==1?"0"+G:G},i:function(){var min=String(date.getMinutes());return min.length==1?"0"+min:min},I:function(){var noDST=new Date("January 1 "+this.Y()+" 00:00:00");return noDST.getTimezoneOffset()==date.getTimezoneOffset()?0:1},j:function(){return date.getDate()},l:function(){return daysLong[date.getDay()]},L:function(){var Y=this.Y();if(Y%4==0&&Y%100!=0||Y%4==0&&Y%100==0&&Y%400==0){return 1}else{return 0}},m:function(){var n=String(this.n());return n.length==1?"0"+n:n},M:function(){return monthsShort[date.getMonth()]},n:function(){return date.getMonth()+1},N:function(){var w=this.w();return w==0?7:w},O:function(){var os=Math.abs(date.getTimezoneOffset());var h=String(Math.floor(os/60));var m=String(os%60);h.length==1?h="0"+h:1;m.length==1?m="0"+m:1;return date.getTimezoneOffset()<0?"+"+h+m:"-"+h+m},P:function(){var O=this.O();return O.substr(0,3)+":"+O.substr(3,2)},r:function(){var r;r=this.D()+", "+this.d()+" "+this.M()+" "+this.Y()+" "+this.H()+":"+this.i()+":"+this.s()+" "+this.O();return r},s:function(){var sec=String(date.getSeconds());return sec.length==1?"0"+sec:sec},S:function(){switch(date.getDate()){case 1:return"st";case 2:return"nd";case 3:return"rd";case 21:return"st";case 22:return"nd";case 23:return"rd";case 31:return"st";default:return"th"}},t:function(){var daysinmonths=[null,31,28,31,30,31,30,31,31,30,31,30,31];if(this.L()==1&&this.n()==2)return 29;return daysinmonths[this.n()]},U:function(){return Math.round(date.getTime()/1e3)},w:function(){return date.getDay()},W:function(){var DoW=this.N();var DoY=this.z();var daysToNY=364+this.L()-DoY;if(daysToNY<=2&&DoW<=3-daysToNY){return 1}if(DoY<=2&&DoW>=5){return new Date(this.Y()-1,11,31).formatDate("W")}var nyDoW=new Date(this.Y(),0,1).getDay();nyDoW=nyDoW!=0?nyDoW-1:6;if(nyDoW<=3){return 1+Math.floor((DoY+nyDoW)/7)}else{return 1+Math.floor((DoY-(7-nyDoW))/7)}},y:function(){var y=String(this.Y());return y.substring(y.length-2,y.length)},Y:function(){if(date.getFullYear){var newDate=new Date("January 1 2001 00:00:00 +0000");var x=newDate.getFullYear();if(x==2001){return date.getFullYear()}}var x=date.getYear();var y=x%100;y+=y<38?2e3:1900;return y},z:function(){var s="January 1 "+this.Y()+" 00:00:00 GMT"+this.O();var t=new Date(s);var diff=date.getTime()-t.getTime();return Math.floor(diff/1e3/60/60/24)},Z:function(){return date.getTimezoneOffset()*-60}};function getSwitch(str){if(switches[str]!=undefined){return switches[str]()}else{return str}}var date;if(time){var date=new Date(time)}else{var date=this}var formatString=input.split("");var i=0;while(i Date: Tue, 13 Dec 2016 05:45:51 +0100 Subject: [PATCH 319/718] Use localStorage first, location.hash later. --- templates/munstrap/static/js/munstrap.js | 40 ++++++++++++++------ templates/munstrap/static/js/munstrap.min.js | 3 +- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/templates/munstrap/static/js/munstrap.js b/templates/munstrap/static/js/munstrap.js index 66c18b7a..21f63c61 100644 --- a/templates/munstrap/static/js/munstrap.js +++ b/templates/munstrap/static/js/munstrap.js @@ -17,15 +17,33 @@ $("div#munin_nodeview_tab>div").each(function (index) { /* * Update the URL with selected tab and active selected tab on page refresh */ -$(document).ready(function () { - if (location.hash) { - $('a[href="' + location.hash + '"]').tab('show'); - } - $(document.body).on("click", "a[data-toggle=tab]", function (event) { - location.hash = this.getAttribute("href"); +(function () { + 'use strict'; + + $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { + var id = $(this).parents('[role="tablist"]').attr('id'); + var key = 'lastTag'; + if (id) { + key += ':' + id; + } + + localStorage.setItem(key, $(e.target).attr('href')); + location.hash = $(e.target).attr('href'); }); -}); -$(window).on('popstate', function () { - var anchor = location.hash || $("a[data-toggle=tab]").first().attr("href"); - $('a[href="' + anchor + '"]').tab('show'); -}); \ No newline at end of file + + $('[role="tablist"]').each(function (idx, elem) { + var id = $(elem).attr('id'); + var key = 'lastTag'; + if (id) { + key += ':' + id; + } + + var lastTab = localStorage.getItem(key); + if (!lastTab) { + lastTab = location.hash; + } + if (lastTab) { + $('[href="' + lastTab + '"]').tab('show'); + } + }); +})(); \ No newline at end of file diff --git a/templates/munstrap/static/js/munstrap.min.js b/templates/munstrap/static/js/munstrap.min.js index 142b24ff..eff493b2 100644 --- a/templates/munstrap/static/js/munstrap.min.js +++ b/templates/munstrap/static/js/munstrap.min.js @@ -1,2 +1 @@ -$("ul#tabs>li>a").each(function(a){a=$(this).attr("href").replace(/[^#\w]/gi,"_");$(this).attr("href",a)});$("div#munin_nodeview_tab>div").each(function(a){a=$(this).attr("id").replace(/[^\w]/gi,"_");$(this).attr("id",a)});$(document).ready(function(){location.hash&&$('a[href="'+location.hash+'"]').tab("show");$(document.body).on("click","a[data-toggle=tab]",function(a){location.hash=this.getAttribute("href")})}); -$(window).on("popstate",function(){var a=location.hash||$("a[data-toggle=tab]").first().attr("href");$('a[href="'+a+'"]').tab("show")}); +$("ul#tabs>li>a").each(function(index){var eid=$(this).attr("href").replace(/[^#\w]/gi,"_");$(this).attr("href",eid)});$("div#munin_nodeview_tab>div").each(function(index){var eid=$(this).attr("id").replace(/[^\w]/gi,"_");$(this).attr("id",eid)});(function(){"use strict";$('a[data-toggle="tab"]').on("shown.bs.tab",function(e){var id=$(this).parents('[role="tablist"]').attr("id");var key="lastTag";if(id){key+=":"+id}localStorage.setItem(key,$(e.target).attr("href"));location.hash=$(e.target).attr("href")});$('[role="tablist"]').each(function(idx,elem){var id=$(elem).attr("id");var key="lastTag";if(id){key+=":"+id}var lastTab=localStorage.getItem(key);if(!lastTab){lastTab=location.hash}if(lastTab){$('[href="'+lastTab+'"]').tab("show")}})})(); \ No newline at end of file From 31a71874f50d4d4604a7bed6984f42636e6e6c10 Mon Sep 17 00:00:00 2001 From: Cristian Deluxe Date: Sat, 28 Jan 2017 01:48:31 +0100 Subject: [PATCH 320/718] Restored deleted image/text styles for warnings --- .../munstrap/static/css/style-munstrap.css | 24 +++++++++++++++++++ .../static/css/style-munstrap.min.css | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/templates/munstrap/static/css/style-munstrap.css b/templates/munstrap/static/css/style-munstrap.css index 2f633db5..ae76f730 100644 --- a/templates/munstrap/static/css/style-munstrap.css +++ b/templates/munstrap/static/css/style-munstrap.css @@ -93,4 +93,28 @@ ul.groupview, ul.groupview ul { -webkit-border-radius: 6px 0 6px 6px; -moz-border-radius: 6px 0 6px 6px; border-radius: 6px 0 6px 6px; +} + +img { + border: 2px solid transparent; +} + +img.warn { + border: 2px solid #8a6d3b; +} + +img.crit { + border: 2px solid #a94442; +} + +img.unkn { + border: 2px solid #ffaa00; +} + +.text-critical { + color: #a94442; +} + +.text-critical:hover { + color: #843534; } \ No newline at end of file diff --git a/templates/munstrap/static/css/style-munstrap.min.css b/templates/munstrap/static/css/style-munstrap.min.css index b3d897ec..8c7da5e5 100644 --- a/templates/munstrap/static/css/style-munstrap.min.css +++ b/templates/munstrap/static/css/style-munstrap.min.css @@ -1 +1 @@ -@media(min-width:992px){.modal-lg{width:940px !important}}body{padding-top:70px}img.i{display:block;margin:10px auto}img.img-zoom{cursor:pointer}div.service-alert{margin-top:10px}img#zoom_image{margin-bottom:15px}.link-domain{font-size:1.4em;color:#606}.link-host{font-size:1.1em;color:purple}ul.groupview,ul.groupview ul{list-style-type:none}.munin-icon{background:url(../img/logo-munin.png) left top;margin-top:-6px;width:35px;height:35px;display:block;float:left}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border:5px solid transparent;border-right-width:0;border-left-color:#ccc;margin-top:5px;margin-right:-10px}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px} \ No newline at end of file +@media(min-width:992px){.modal-lg{width:940px !important}}body{padding-top:70px}img.i{display:block;margin:10px auto}img.img-zoom{cursor:pointer}div.service-alert{margin-top:10px}img#zoom_image{margin-bottom:15px}.link-domain{font-size:1.4em;color:#606}.link-host{font-size:1.1em;color:purple}ul.groupview,ul.groupview ul{list-style-type:none}.munin-icon{background:url(../img/logo-munin.png) left top;margin-top:-6px;width:35px;height:35px;display:block;float:left}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border:5px solid transparent;border-right-width:0;border-left-color:#ccc;margin-top:5px;margin-right:-10px}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}img{border:2px solid transparent}img.warn{border:2px solid #8a6d3b}img.crit{border:2px solid #a94442}img.unkn{border:2px solid #fa0}.text-critical{color:#a94442}.text-critical:hover{color:#843534} \ No newline at end of file From 8b27b99d0e2cc42abcc9f7dccfb8c27648727b31 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Sat, 28 Jan 2017 08:41:24 +0100 Subject: [PATCH 321/718] Integrated responsive graph columns into official template --- templates/official/munin-nodeview.tmpl | 18 ++++++++++++++++-- templates/official/partial/head.tmpl | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/templates/official/munin-nodeview.tmpl b/templates/official/munin-nodeview.tmpl index e9b852df..a1b5f5ba 100644 --- a/templates/official/munin-nodeview.tmpl +++ b/templates/official/munin-nodeview.tmpl @@ -17,14 +17,28 @@ width="" height=""/> - "> + "> warncrit src="" alt="" width="" height=""/> - + "> + warncrit + src="" + alt="" + width="" + height=""/> + + "> + warncrit + src="" + alt="" + width="" + height=""/> + + diff --git a/templates/official/partial/head.tmpl b/templates/official/partial/head.tmpl index 44a8157f..e60d69f9 100644 --- a/templates/official/partial/head.tmpl +++ b/templates/official/partial/head.tmpl @@ -18,4 +18,23 @@ /static/favicon.ico" /> /static/favicon.ico"/> + + From dc4f97c4db6ac5def4c74d863265ee74477300d1 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Sat, 28 Jan 2017 08:44:53 +0100 Subject: [PATCH 322/718] Removed responsive template folder Integrated responsive 3rd and 4th graph column into official template. The responsive template is redundant and has therefore been deleted. --- templates/official/partial/head.tmpl | 6 +- templates/responsive/munin-categoryview.tmpl | 55 ------- .../responsive/munin-comparison-day.tmpl | 44 ------ .../responsive/munin-comparison-month.tmpl | 42 ------ .../responsive/munin-comparison-week.tmpl | 44 ------ .../responsive/munin-comparison-year.tmpl | 42 ------ templates/responsive/munin-domainview.tmpl | 72 --------- templates/responsive/munin-dynazoom.tmpl | 82 ---------- templates/responsive/munin-nodeview.tmpl | 49 ------ templates/responsive/munin-overview.tmpl | 68 --------- templates/responsive/munin-problemview.tmpl | 141 ------------------ templates/responsive/munin-serviceview.tmpl | 105 ------------- .../responsive/partial/bottom_navigation.tmpl | 33 ---- templates/responsive/partial/footer.tmpl | 7 - .../responsive/partial/generated_by.tmpl | 5 - templates/responsive/partial/head.tmpl | 40 ----- .../responsive/partial/logo_navigation.tmpl | 38 ----- .../partial/logo_navigation_comparison.tmpl | 33 ---- .../partial/logo_navigation_problem.tmpl | 12 -- templates/responsive/partial/logo_path.tmpl | 7 - templates/responsive/partial/navigation.tmpl | 30 ---- templates/responsive/partial/path.tmpl | 1 - 22 files changed, 3 insertions(+), 953 deletions(-) delete mode 100644 templates/responsive/munin-categoryview.tmpl delete mode 100644 templates/responsive/munin-comparison-day.tmpl delete mode 100644 templates/responsive/munin-comparison-month.tmpl delete mode 100644 templates/responsive/munin-comparison-week.tmpl delete mode 100644 templates/responsive/munin-comparison-year.tmpl delete mode 100644 templates/responsive/munin-domainview.tmpl delete mode 100644 templates/responsive/munin-dynazoom.tmpl delete mode 100644 templates/responsive/munin-nodeview.tmpl delete mode 100644 templates/responsive/munin-overview.tmpl delete mode 100644 templates/responsive/munin-problemview.tmpl delete mode 100644 templates/responsive/munin-serviceview.tmpl delete mode 100644 templates/responsive/partial/bottom_navigation.tmpl delete mode 100644 templates/responsive/partial/footer.tmpl delete mode 100644 templates/responsive/partial/generated_by.tmpl delete mode 100644 templates/responsive/partial/head.tmpl delete mode 100644 templates/responsive/partial/logo_navigation.tmpl delete mode 100644 templates/responsive/partial/logo_navigation_comparison.tmpl delete mode 100644 templates/responsive/partial/logo_navigation_problem.tmpl delete mode 100644 templates/responsive/partial/logo_path.tmpl delete mode 100644 templates/responsive/partial/navigation.tmpl delete mode 100644 templates/responsive/partial/path.tmpl diff --git a/templates/official/partial/head.tmpl b/templates/official/partial/head.tmpl index e60d69f9..82ceb82e 100644 --- a/templates/official/partial/head.tmpl +++ b/templates/official/partial/head.tmpl @@ -20,17 +20,17 @@ - - diff --git a/templates/responsive/partial/logo_navigation.tmpl b/templates/responsive/partial/logo_navigation.tmpl deleted file mode 100644 index 6351d0d1..00000000 --- a/templates/responsive/partial/logo_navigation.tmpl +++ /dev/null @@ -1,38 +0,0 @@ - - diff --git a/templates/responsive/partial/logo_navigation_comparison.tmpl b/templates/responsive/partial/logo_navigation_comparison.tmpl deleted file mode 100644 index 7699fff5..00000000 --- a/templates/responsive/partial/logo_navigation_comparison.tmpl +++ /dev/null @@ -1,33 +0,0 @@ - diff --git a/templates/responsive/partial/logo_navigation_problem.tmpl b/templates/responsive/partial/logo_navigation_problem.tmpl deleted file mode 100644 index 6bdcdcfa..00000000 --- a/templates/responsive/partial/logo_navigation_problem.tmpl +++ /dev/null @@ -1,12 +0,0 @@ - - diff --git a/templates/responsive/partial/logo_path.tmpl b/templates/responsive/partial/logo_path.tmpl deleted file mode 100644 index d29ed0df..00000000 --- a/templates/responsive/partial/logo_path.tmpl +++ /dev/null @@ -1,7 +0,0 @@ -

Overview

- - - -

- - diff --git a/templates/responsive/partial/navigation.tmpl b/templates/responsive/partial/navigation.tmpl deleted file mode 100644 index 85895d21..00000000 --- a/templates/responsive/partial/navigation.tmpl +++ /dev/null @@ -1,30 +0,0 @@ - - diff --git a/templates/responsive/partial/path.tmpl b/templates/responsive/partial/path.tmpl deleted file mode 100644 index fb27324c..00000000 --- a/templates/responsive/partial/path.tmpl +++ /dev/null @@ -1 +0,0 @@ - :: ">">Overview From f2a2623d8ec4097d2cc30dcd1ba2d1a77e7e30cc Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Sun, 29 Jan 2017 07:57:58 +0100 Subject: [PATCH 323/718] added comments to media query css --- templates/official/partial/head.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/official/partial/head.tmpl b/templates/official/partial/head.tmpl index 82ceb82e..1a7c2342 100644 --- a/templates/official/partial/head.tmpl +++ b/templates/official/partial/head.tmpl @@ -19,16 +19,19 @@ /static/favicon.ico"/>